【问题标题】:Parallel.For loop exits too soonParallel.For 循环退出太快
【发布时间】:2019-02-28 13:44:04
【问题描述】:

我正在尝试将所有任务并行添加到列表中,然后等待它们。代码:

List<Task<bool>> tasks = new List<Task<bool>>();                

Parallel.For(0, 500, file =>
{
  tasks.Add(SomeTask());
});

Console.WriteLine("Total tasks = " + tasks.Count);

当我执行以下代码时,有时我会得到任务列表(tasks.Count)的大小为 493 或 500 或 498。但这不是确定性的。我该怎么办?

【问题讨论】:

  • 您应该停止在这些任务中使用共享的非线程安全列表。 List&lt;T&gt; 不是线程安全的。要么在你的代码周围添加一个锁来操作共享列表,使用适当的同步集合,或者做一些完全不同的不需要列表的事情(问题中的信息太少,最后一个选项太模糊,无法给出更多提示为)。
  • 基本上,for-loop 并没有很快退出,但是列表添加没有同步,因此一些更新丢失了。很可能,如果您要查看其中的任务,您还会发现其他奇怪的东西,例如重复条目或空引用。
  • List&lt;T&gt;(在您的情况下为List&lt;Task&lt;bool&gt;&gt;不是线程安全的;在Parallel.For 内执行Add危险的,很可能会导致不可预知的行为
  • 尝试 PLinq (Parallel Linq) 而不是 Parallel.For: List&lt;Task&lt;bool&gt;&gt; tasks = Enumerable.Range(0, 500).AsParallel().Select(file =&gt; SomeTask()).ToList();
  • 除了关于 List 和线程安全的 cmets 之外,使用 parallel.For() 将 500 个项目添加到 List 中绝对没有任何好处。

标签: c# multithreading task parallel.for


【解决方案1】:

任务返回方法(例如,SomeTask)通常会非常快地返回,并且并行化它们的调用通常会导致执行速度变慢

使用(非并行)LINQ 构建任务集合更为正常:

var tasks = Enumerable.Range(0, 500).Select(file => SomeTask()).ToList();
Console.WriteLine("Total tasks = " + tasks.Count);

如果您的任务返回方法有一些 CPU 绑定代码,并且您确实需要在线程池线程上运行 SomeTask,那么您可以使用并行性。一种简单的方法是将调用包装在Task.Run

var tasks = Enumerable.Range(0, 500).Select(file => Task.Run(() => SomeTask())).ToList();
Console.WriteLine("Total tasks = " + tasks.Count);

【讨论】:

    【解决方案2】:

    您拥有所谓的race condition。您在没有同步的情况下从多个线程访问列表,这会导致它处于奇怪的状态。

    您可以改用 ConcurrentStack 之类的线程安全对象(或 ConcurrentBag 或 ConcurrentQueue,具体取决于您对集合的处理方式):

    ConcurrentStack<Task<bool>> tasks = new ConcurrentStack<Task<bool>>();                
    
    Parallel.For(0, 500, file =>
    {
      tasks.Push(SomeTask());
    });
    
    Console.WriteLine("Total tasks = " + tasks.Count);
    

    【讨论】:

    • 为什么是并发栈?最接近列表的集合是并发包。
    • System.Collections.Concurrent 命名空间中可用的线程安全集合是:ConcurrentDictionary&lt;TKey,TValue&gt;BlockingCollection&lt;T&gt;ConcurrentStack&lt;T&gt;ConcurrentQueue&lt;T&gt;ConcurrentBag&lt;T&gt;IProducerConsumerCollection&lt;T&gt;
    • Stack 最适合我。几乎所有并发集合都可以满足他的要求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多