1

我继承了 C#/XAML/Win 8 应用程序。有一些代码设置为每 n 秒运行一次。

设置它的代码是:

if(!_syncThreadStarted)
{
    await Task.Run(() => SyncToDatabase());
    _syncThreadStarted = true;
}

上面的代码运行一次。

然后在 SyncToDatabase() 我们有:

while (true)
{
    DatabaseSyncer dbSyncer = new DatabaseSyncer();
    await dbSyncer.DeserializeAndUpdate();

    await Task.Delay(10); // after elapsed time re-run above code
}

DeserializeAndUpdate 方法查询内存中的对象集合并将这些对象推送到 Web 服务。

有时,向 Web 服务发送请求的时间比预期的要长,这意味着发送了重复的项目。

问题:有没有办法让我可以在 SyncToDatabase() 方法中停止/中止/销毁线程或某种类型的线程池/后台工作程序,然后在完成后初始化/启动它?这将确保在前一个请求仍处于未决状态时不会触发后续请求。

编辑:我对线程不是很了解,但我想要的逻辑是:

创建每 x 秒运行一次方法的线程,当它启动该线程时停止“每 x 秒运行”部分,在线程完成后再次启动“每 x 秒运行”部分。

例如,如果线程在上午 10:01:30 开始并且直到上午 10:01:39(9 秒)才完成,那么下一个线程应该在上午 10:01:44(工作完成后 5 秒)开始 - 这有意义吗?我不想同时运行 2 个或更多线程。

这是我的上述代码:

var period = TimeSpan.FromSeconds(5);
var completed = true;
ThreadPoolTimer syncTimer = ThreadPoolTimer.CreatePeriodicTimer(async (source) =>
{
    // stop further threads from starting (in case this work takes longer than var period)
    syncTimer.Cancel();
    DatabaseSyncer dbSyncer = new DatabaseSyncer();
    await dbSyncer.DeserializeAndUpdate(); // makes webservices calls

    Dispatcher.RunAsync(CoreDispatcerPriority.High, async () =>
    {
        // Update UI
    }

    completed = true;
}, period,
(source) =>
{
    if(!completed)
    {
        syncTimer.Cancel(); // not sure if this is correct...
    }
}

谢谢,安德鲁)

4

2 回答 2

2

这并不特定于 Windows 8。通常Task.Run用于 CPU 密集型工作,将其卸载到池线程并保持 UI(或核心服务循环)响应。在您的情况下,据我所知,主要有效负载是dbSyncer.DeserializeAndUpdate,它已经是异步的,并且很可能是网络 IO 绑定的,而不是 CPU 绑定的。

此外,原始代码的作者_syncThreadStarted = trueawait Task.Run(() => SyncToDatabase()). 这没有意义,因为池线程上的工作在执行时已经完成_syncThreadStarted = true,这要归功于await.

要取消内部循环,SyncToDatabase您可以使用Task Cancellation PatternSyncToDatabase本身是一种方法async吗?我想是这样,因为await. while loop鉴于此,调用它的代码可能如下所示:

if(_syncTask != null && !_syncTask.IsCompleted)
{
    _ct.Cancel();
    // here you may want to make sure that the pending task has been fully shut down, 
    // keeping possible re-entrancy in mind
    // See: https://stackoverflow.com/questions/18999827/a-pattern-for-self-cancelling-and-restarting-task
    _syncTask = null;
}
_ct = new CancellationTokenSource();
// _syncTask = SyncToDatabase(ct.Token); // do not await
// edited to run on another thread, as requested by the OP
var _syncTask = Task.Run(async () => await SyncToDatabase(ct.Token), ct.Token);
_syncThreadStarted = true;

SyncToDatabase可能看起来像:

async Task SyncToDatabase(CancellationToken token)
{
    while (true)
    {
        token.ThrowIfCancellationRequested();
        DatabaseSyncer dbSyncer = new DatabaseSyncer();
        await dbSyncer.DeserializeAndUpdate();
        await Task.Delay(10, token); // after elapsed time re-run above code
    }
}

查看此答案以获取有关如何取消和重新启动任务的更多详细信息。

于 2013-11-08T07:54:43.787 回答
0

我可能误解了这个问题,但是执行SynchToDatabase()将等待await dbSyncer.DeserializeAndUpdaet()(由于await关键字,go figure ;)) 的完成,然后再执行继续,然后延迟 10 毫秒(你想要 10 毫秒还是你的意思是 10 seconds?参数forTask.Delay是毫秒),然后循环回来重新执行该DbSyncer方法,所以我没有看到问题。

于 2013-11-08T07:38:07.727 回答