【问题标题】:Async/await tasks and WaitHandle异步/等待任务和 WaitHandle
【发布时间】:2012-08-24 13:15:53
【问题描述】:

假设我有 10N 个项目(我需要通过 http 协议获取它们),在代码中启动 N 个任务来获取数据,每个任务依次获取 10 个项目。我将这些项目放在ConcurrentQueue<Item> 中。之后,项目会以线程不安全的方法一一处理。

async Task<Item> GetItemAsync()
{
    //fetch one item from the internet
}

async Task DoWork()
{
    var tasks = new List<Task>();
    var items = new ConcurrentQueue<Item>();
    var handles = new List<ManualResetEvent>();

    for i 1 -> N
    {
        var handle = new ManualResetEvent(false);
        handles.Add(handle);

        tasks.Add(Task.Factory.StartNew(async delegate
        {
            for j 1 -> 10
            {
                var item = await GetItemAsync();
                items.Enqueue(item);
            }
            handle.Set();
        });
    }

    //begin to process the items when any handle is set
    WaitHandle.WaitAny(handles);

    while(true)
    {
         if (all handles are set && items collection is empty) //***
           break; 
         //in another word: all tasks are really completed

         while(items.TryDequeue(out item))          
         {
              AThreadUnsafeMethod(item);    //process items one by one
         }
    }
}

我不知道如果条件可以放在标记为*** 的语句中。我这里不能使用Task.IsCompleted属性,因为我在任务中使用了await,所以任务很快就完成了。还有一个bool[]表示任务是否执行到最后看起来真的很难看,因为我认为ManualResetEvent可以做同样的工作。谁能给我一个建议?

【问题讨论】:

    标签: c# asynchronous task waithandle async-await


    【解决方案1】:

    嗯,您可以自己构建它,但我认为使用TPL Dataflow 会更容易。

    类似:

    static async Task DoWork()
    {
      // By default, ActionBlock uses MaxDegreeOfParallelism == 1,
      //  so AThreadUnsafeMethod is not called in parallel.
      var block = new ActionBlock<Item>(AThreadUnsafeMethod);
    
      // Start off N tasks, each asynchronously acquiring 10 items.
      // Each item is sent to the block as it is received.
      var tasks = Enumerable.Range(0, N).Select(Task.Run(
          async () =>
          {
            for (int i = 0; i != 10; ++i)
              block.Post(await GetItemAsync());
          })).ToArray();
    
      // Complete the block when all tasks have completed.
      Task.WhenAll(tasks).ContinueWith(_ => { block.Complete(); });
    
      // Wait for the block to complete.
      await block.Completion;
    }
    

    【讨论】:

      【解决方案2】:

      您可以执行一个超时为零的 WaitOne 来检查状态。像这样的东西应该可以工作:

      if (handles.All(handle => handle.WaitOne(TimeSpan.Zero)) && !items.Any())
          break;
      

      http://msdn.microsoft.com/en-us/library/cc190477.aspx

      【讨论】:

      • “阻塞当前线程,直到当前实例接收到信号”,因此您将有 10 个阻塞线程等待信号。
      • @Sebastian "备注:如果超时时间为零,则该方法不会阻塞。它会测试等待句柄的状态并立即返回。"
      • @jaggedSpire 好的,这是我错过的信息。谢谢,现在这是我检查 WaitHandles 的首选解决方案。
      【解决方案3】:

      谢谢大家。最后我发现CountDownEvent 非常适合这种场景。一般的实现是这样的:(供他人参考)

      for i 1 -> N
      {
          //start N tasks
          //invoke CountDownEvent.Signal() at the end of each task
      }
      
      //see if CountDownEvent.IsSet here
      

      【讨论】:

        猜你喜欢
        • 2013-02-10
        • 1970-01-01
        • 1970-01-01
        • 2014-03-18
        • 1970-01-01
        • 2014-09-06
        • 2017-07-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多