我是否应该在每个线程中使用并发集合而不是传统集合?

时间:2018-12-24 12:03:43

标签: c# .net multithreading

我基本上是试图通过诸如

之类的方法调用将某些项目(大约T的100000个实例)插入到static List<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>之类的并发集合的信息,是否应该更改代码以使用并发集合或保持实现不变?我是说哪种编程最佳实践更受青睐?

1 个答案:

答案 0 :(得分:1)

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

通过使SomeMethod中的集合成为同步(并发/线程安全)集合,实际上是在引入开销,以保护自己免受永远不会发生的情况的影响。现在,每个面包师必须在触摸他们的任何工具或原料之前,问“还有其他人在用我的汤匙吗?”并声明“我正在使用汤匙!”并声明“我不再使用汤匙”。实际上,实际上有4个汤匙,没有其他人可以触摸您的汤匙,蛋糕粉或烤箱。因此,无需同步即可解决问题。

继续类推,我们的想法是让每个面包师烘焙一个4层蛋糕的一层,然后当他们全部完成后,再由第五个面包师将所有4层蛋糕组装成一个最终的蛋糕。

>

在这种情况下,您是第五位面包师,并且您可以运行节目。

您指示另外4个面包师各自烘焙蛋糕。为此,您可以并行调用a.SomeMethod()b.SomeMethod()c.SomeMethod()d.SomeMethod调用Parallel.Invoke。该方法完成后,您将知道“所有四个蛋糕都准备就绪”。此时,您将完成将蛋糕堆叠成单层蛋糕的剩余工作。您致电:

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内部同步,让您知道所有任务何时完成。并在另一个线程中进行合并操作(可以是第五个工作线程,也可以只是主线程,具体取决于程序中发生的其他事情)。