用Monitor.Wait()和Monitor.Pulse()暂停/恢复长时间运行的任务(while(true))是否正确?

时间:2015-01-08 11:24:54

标签: c# multithreading task-parallel-library async-await

我有以下代码,它充当异步消费者:

private readonly Object sync;

private async void ConsumeAsync()
{
    // The reason why I have an await here is to yield the control to the caller, e.g: if an exception happens inside the Listen method, it's being propagated and can be handled in caller codes.
    await Task.Run(() => this.Listen());
}

private void Listen()
{
    ...
    while (true)
    {
        lock (sync)
        {
            while (<blocking-condition>)
            {
                Monitor.Wait(sync);
            }
        }

        // Consuming happens here
    }
}

// Changing the blocking condition happens outside the infinite loop with proper Monitor.Pulse usage

我的设计问题:

  1. 我有一个async void fire-and-forget方法,如果没有消耗,必须阻止它。这是一种正确的方法吗?
  2. 我是否必须为任务使用LongRunning选项?
  3. 我必须使用CancellationToken吗?
  4. 我是否正确地将异常传播到更高级别的异常处理程序?

1 个答案:

答案 0 :(得分:1)

对于这个论坛,你的四个问题中有两个过于宽泛。只有你可以决定这是否是一个正确的方法&#34;为您的方案。同样,是否使用CancellationToken的问题取决于您需要哪些功能。如果你需要能够取消操作,这可能是一个很好的方法,但不是唯一的方法,而且这里没有足够的细节知道什么是最好的。你也不能提供足够的细节,同时保持这个论坛的问题足够狭窄。

当然,如果您不需要取消操作,那么该问题的答案就是&#34;不,您不需要使用CancellationToken&# 34; :)

至于其他两个问题,只有一个问题有明确答案:

  1. &#34;我是否必须为任务使用LongRunning选项?&#34;
  2. 不,你 。但强烈建议实际上长期运行的任务。这将有助于任务调度框架正确管理您的任务的线程。

    1. &#34;我是否正确地将异常传播到更高级别的异常处理程序?&#34;
    2. 你是否怀疑。由于您的ConsumeAsync()方法为async void,因此您显然无法等待它。执行此类操作的自然方法是编写更像这样的代码:

      private Task ConsumeAsync()
      {
          return Task.Run(() => this.Listen());
      }
      
      async Task SomeMethodSomewhereElse()
      {
          try
          {
              await ConsumeAsync();
          }
          catch (...)
          {
              // An exception thrown from your long-running task will be caught here
          }
      }
      

      请注意,ConsumeAsync()方法本身并不需要async。您只需返回已创建的Task,并且将完全相同(更好,更有效......但主要的是不添加完全不必要的额外代码)。

      你编写它的方式,你将不得不去做很多额外的工作来捕捉抛出的异常(即直接从SynchronizationContext处理它)。也许你这样做了,也许你没有。你没有显示该代码,所以我假设你没有。