【问题标题】:Wait Handles and the System.Threading.Tasks namespace等待句柄和 System.Threading.Tasks 命名空间
【发布时间】:2012-02-23 02:17:52
【问题描述】:

当我在System.Threading.Tasks 命名空间中使用Parallel.ForEachParallel.For 时,我可以假设所有线程在继续执行之前都已同步吗?

有没有类似的东西

WaitHandle.WaitAll(...);

继续?

所以如果我调用类似的东西

Parallel.ForEach(collection, DoSomethingWithObject);

我能否确定每次对DoSomethingWithObject 的调用都已完成ForEach 返回之前,还是我需要自己使用WaitHandles

【问题讨论】:

    标签: c# .net multithreading task


    【解决方案1】:

    为了扩展 Phong 的正确答案,Parallel.For 和 Parallel.ForEach 在内部创建一个所谓的 ParallelXXXReplicationTask,它同步运行并生成 X 个子任务,其中 X 是并发级别的有效数量(默认 Environment.ProcessorCount)。每个子任务与 Semphore 同步,一旦设置(计数为 0)就会释放父任务,这确保父任务的执行只有在所有子任务完成后才会返回(或崩溃/停止,这是通过同步设置确定的)。由于 parentTask 同步运行并正在等待。您可以确保每当您对 Parallel 方法的调用返回时,所有迭代都已有效完成。

    【讨论】:

      【解决方案2】:

      所有任务将在Parallel.ForEach() 返回时完成,除非专门调用Stop() 以中止并行执行。在这种情况下,Parallel.ForEach() 返回的ParallelLoopState 对象的.IsCompleted 属性将为false

      你可以在这里阅读:

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

      http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallelloopresult.aspx

      您还可以编写一个小型测试应用程序,其中DoSomethingWithObject() 是一个运行时间很长的操作,或者调用Thread.Sleep() 来验证这一点。执行将阻塞,直到所有线程都完成睡眠。

      示例:

      Parallel.ForEach(Enumerable.Range(1, 8),
          i =>
          {
              Thread.Sleep(5000);
              Console.WriteLine(i + " done!");
          });
      Console.WriteLine("All done!");
      

      【讨论】:

      • 谢谢。我写了一个测试,DoSomethingWithObject 休眠了 3 秒。行为符合预期。
      【解决方案3】:

      我能否确定对 DoSomethingWithObject 的每次调用都已完成 在 ForEach 返回之前,还是我需要自己使用 WaitHandles?

      Parallel.ForEach 是一个阻塞调用。在每次迭代完成之前它不会返回。

      是否有与WaitHandle.WaitAll(...); 等价的东西在进行中?

      是的,或多或少。它实际上不会使用WaitHandle.WaitAll,因为1)它的效率非常低,2)它不可扩展,因为它有64个句柄限制。有一些非常可扩展的技术可以等待多个任务完成。我的最爱之一是使用CountdownEvent1

      这是它的工作原理。发挥你的想象力,假装Parallel.ForEach 被扩展为以下代码。2

      var finished = new CountdownEvent(1);
      foreach (var item in collection)
      {
        var capture = item;
        finished.AddCount();
        Task.Factory.StartNew(
          () =>
          {
            try
            {
              DoSomething(capture);
            }
            finally
            {
              finished.Signal();
            }
          });
      }
      finished.Signal();
      finished.Wait();
      

      1我并不是说这是Parallel.ForEach 使用的技术,但它可能是类似的东西。

      2实际上Parallel.ForEach 的实现方式要复杂得多。我只是想帮助您想象它如何等待每次迭代完成。

      【讨论】:

        【解决方案4】:

        Parallel.ForEach()Parallel.For() 将阻塞直到其所有任务结束,因为它们成功完成或因为它们被取消。所以是的,在继续执行之前所有线程都已同步。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-01-29
          相关资源
          最近更新 更多