只需要第一个任务,忽略其余任务

时间:2013-12-22 13:25:31

标签: c# asynchronous async-await

我想以指定的间隔调用任务。并且避免调用新任务,除非最后一个任务已经完成。

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();
    }
}

我如何实现这一目标?有关更好模式的任何建议吗?

1 个答案:

答案 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来避免跨线程的IsSetSet()调用之间的竞争。第二种方法将检查代码保持在一起,代价是暂时运行另一个任务(除非这种情况很常见,我可能会采用这种方法来代码本地)。