我想以指定的间隔调用任务。并且避免调用新任务,除非最后一个任务已经完成。
private async void OnTimerTick(object sender, object e)
{
if (_criticalSection.IsEntered()) return; // only allow 1 at any given time, ignore the rest
using (var section = await _criticalSection.EnterAsync())
{
await update();
}
}
我如何实现这一目标?有关更好模式的任何建议吗?
答案 0 :(得分:1)
关键部分(如Window的互斥锁)用于互斥:只允许单个线程进入代码路径。
但那并不是你想要做的事情:你需要一些可以告诉你是否有事情发生的事情。
更好的方法是手动重置事件:在任务开始时设置它(也称为信号)并在结束时重置。然后,您可以通过等待正常Window事件的超时为零来检查是否发出信号,或者使用适用于其他类型事件的成员进行检查。
由于这似乎只是一个过程,因此一个好的起点是System.Threading.ManualRestEventSlim
。使用类似:
// One off initialisation somewhere at class scope
private static ManualResetEventSlim taskRunning = new ManualResetEventSlim();
private static object taskLock = new Object();
// code called from the timer, do in a lock to avoid race conditions with two
// or more threads call this.
lock (taskLock) {
if (!taskRunning.IsSet) {
StartTheTask(); // assuming this does not return until task is running.
}
}
// At the outermost scope of the code in the task:
try {
Debug.Assert(!taskRunning.IsSet); // Paranoia is good when doing threading
taskRunning.Set();
// Task impementation
} finally {
Debug.Assert(taskRunning.IsSet); // Paranoia is good when doing threading
taskRunning.Reset();
}
另一种方法是始终启动任务,但让它检查事件,如果设置则立即退出。这仍然需要lock
来避免跨线程的IsSet
和Set()
调用之间的竞争。第二种方法将检查代码保持在一起,代价是暂时运行另一个任务(除非这种情况很常见,我可能会采用这种方法来代码本地)。