我继承了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秒运行一次”部分。
E.g。如果线程在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...
}
}
谢谢, 安德鲁)
答案 0 :(得分:2)
这不是特定于Windows 8.通常Task.Run
用于CPU绑定工作,将其卸载到池线程并保持UI(或核心服务循环)响应。在你的情况下,据我所知,主要有效载荷是dbSyncer.DeserializeAndUpdate
,它已经是异步的,很可能是网络IO绑定的,而不是CPU绑定的。
此外,原始代码的作者在_syncThreadStarted = true
之后await Task.Run(() => SyncToDatabase())
。这没有意义,因为在执行_syncThreadStarted = true
时,池线程上的工作已经完成了,这要归功于await
。
要取消SyncToDatabase
内的循环,您可以使用Task Cancellation Pattern。 SyncToDatabase
本身是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
}
}
检查this answer以获取有关如何取消和重新启动任务的更多详细信息。
答案 1 :(得分:0)
我可能误解了这个问题,但是SynchToDatabase()
的执行会等到await dbSyncer.DeserializeAndUpdaet()
完成后(由于await
关键字,去图;))然后执行延续然后会延迟10毫秒(你想要10毫秒还是10秒?Task.Delay
的参数是毫秒),然后循环回来重新执行DbSyncer
方法,所以我不知道看不出问题了。