首页 > 解决方案 > 我应该更喜欢并发集合而不是每个线程的传统集合吗?

问题描述

我基本上是在尝试static List<T>通过方法调用将一些项目(大约 100000 个 T 实例)插入(Common.VariablesList),例如

a.SomeMethoad(); b.SomeMethod; c.SomeMethod; d.SomeMethod();

a, b, c & d 继承自一个公共抽象类。我向它们的父抽象类添加了一个实例属性VariablesList<T>,并开始将项目插入实例列表而不是公共列表,以便我可以使用 Parallel.Invoke 并行调用这些方法。之后Parallel.Invoke,我只是将所有这些列表添加到 Common.VariableList。

Common.VariableList.AddRange(a.VariableList);
Common.VariableList.AddRange(b.VariableList);
Common.VariableList.AddRange(c.VariableList);
Common.VariableList.AddRange(d.VariableList);

我最近阅读了有关并发集合的信息,例如ConcurrentBag<T>,我应该更改代码以使用并发集合还是保持实现不变?我的意思是哪一个在编程的最佳实践中更受青睐?

标签: c#.netmultithreading

解决方案


因此,假设每个都SomeMethod使用自己的数据对不同的对象进行操作,那么使用 Parallel.Invoke 同时调用其中的 4 个是没有危险的。您不需要进行任何同步、编组或锁定。这就像告诉 4 个面包师每人烤 1 个蛋糕,每个蛋糕都有自己独立的配料、器具和烤箱。他们可以完全独立工作。

通过在SomeMethod同步(并发/线程安全)集合中创建集合,您实际上是在引入开销来保护自己免受永远不会发生的情况的影响。每个面包师现在必须在接触他们的任何工具或配料之前,问“还有其他人在用我的勺子吗?” 并宣布“我在用我的勺子!” 并宣布“我不再使用我的勺子”。实际上,有 4 个勺子,没有其他人会碰你的勺子、蛋糕粉或烤箱。因此,无需同步即可工作。

继续进行类比,假设这个想法是让每个面包师烘烤一层 4 层蛋糕,然后当他们全部完成后,第五个面包师将所有 4 层组装成最终蛋糕。

在这种情况下,你是第五个面包师,你来主持节目。

您指示其他 4 位面包师分别烘烤他们的蛋糕。a.SomeMethod()您可以通过使用、b.SomeMethod()c.SomeMethod()和并行调用 Parallel.Invoke 来做到这一点d.SomeMethod。当这个方法完成后,你就知道“四个蛋糕都准备好了”。此时,您将完成将蛋糕堆叠成单层蛋糕的剩余工作。你打电话:

Common.VariableList.AddRange(a.VariableList);
Common.VariableList.AddRange(b.VariableList);
Common.VariableList.AddRange(c.VariableList);
Common.VariableList.AddRange(d.VariableList);

这是我的意思的完整示例:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

class Baker
{
    List<string> myList = new List<string>();

    public string Bake(string flavor)
    {
        for (int i = 0; i < 5; ++i)
        {
            myList.Add(flavor); // free to modify local data without synchronization
        }
        return string.Join("", myList);
    }
}
class Program
{
    static string Icing(string layer)
    {
        return new string('~', layer.Length) + Environment.NewLine + layer + Environment.NewLine;
    }

    static void Main(string[] args)
    {
        Baker a = new Baker();
        Baker b = new Baker();
        Baker c = new Baker();
        Baker d = new Baker();

        string cake_a = null;
        string cake_b = null;
        string cake_c = null;
        string cake_d = null;

        Parallel.Invoke(
            () => { cake_a = a.Bake("*cherry*"); },
            () => { cake_b = b.Bake("*orange*"); },
            () => { cake_c = c.Bake("*banana*"); },
            () => { cake_d = d.Bake("*choco**"); }
            );
        string layer_cake = Icing(cake_a) + Icing(cake_b) + Icing(cake_c) + Icing(cake_d);

        Console.WriteLine(layer_cake);
    }
}

所以希望这说明最好使用每个对象的非同步集合来保持低开销(不需要与其他面包师交谈,因为他们没有使用你的东西)并且只依赖于 Parallel.Invoke 如何在内部同步到让您知道所有任务何时完成。并在另一个线程中进行合并操作(可能是第五个工作线程,或者只是主线程,这取决于程序中发生的其他事情。)


推荐阅读