【问题标题】:Using Parallel.ForEach<T> to add to a new List<T>使用 Parallel.ForEach<T> 添加到新的 List<T>
【发布时间】:2015-07-04 18:49:07
【问题描述】:

使用 Parallel.ForEach 等并行化的最佳方法是什么,这样我可以快速迭代集合并将项目添加到新列表,而不会违反线程安全,而是使用多个服务器内核和大量 RAM 的性能增益?

public List<Leg> FetchLegs(Trip trip)
{
    var result = new List<Leg>();

    try
    {
        // get days
        var days = FetchDays(trip);

        // add each day's legs to result
        Parallel.ForEach<Day>(days, day =>
        {
            var legs = FetchLegs(day);
            result.AddRange(legs);
        });

        // sort legs by scheduledOut
        result.Sort((x, y) => x.scheduledOut.Value.CompareTo(y.scheduledOut.Value));

    }
    catch (Exception ex)
    {
        SiAuto.Main.LogException(ex);
        System.Diagnostics.Debugger.Break();
    }

    return result;
}

【问题讨论】:

  • 当前形式的 result.AddRange(legs) 不是线程安全的,因为 List 不是线程安全的结构,请在并行循环中使用 ConcurrentQueue 之类的东西,可以使用 ToList() 将其转换为 List .否则并行代码看起来不错

标签: c# multithreading parallel-processing task-parallel-library .net-4.5


【解决方案1】:

最简单的解决方案是使用PLINQ 及其扩展方法,如ToListToDictionary。示例:

var result = days.AsParallel().SelectMany(d => FetchLegs(d))
                 .OrderBy(l => l.scheduledOut.Value).ToList();

【讨论】:

  • 您需要 ForAll 来执行 AsParallel 后的操作,因为 OP 需要使用 AddRange 将 IEnumerable 创建的后 FetchLegs 方法添加到结果列表中,当前的解决方案将导致 List> 作为最终结果,当预期是 List
  • 你可以抛出一个 OrderBy() 来替换 Sort() 虽然我不确定这是否会从 PLinq 中受益。
【解决方案2】:

除了在并行循环中使用 ConcurrentQueue 之类的线程安全结构来实现线程安全之外,将评论转移到答案中,或者您可以考虑使用自定义线程安全列表,请查看我的以下帖子 - Thread safe List。事实上,你可以考虑用ReaderWriterSlim lock 代替普通的lock。 另一个重要的一点是,您可以考虑使用ParallelOptions MaxDegreeOfParallelism,检查here 的值等于Environment.ProcessorCount,这样Parallel.ForEach 分区器就可以很好地工作,而不是为集合中的每个数据点创建一个任务一开始会根据系统的逻辑核数来做,有效减少线程上下文切换,提高应用效率

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-02
    • 2014-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多