Parallel.For 中 StringBuilder 的线程安全

Thread Safety of StringBuilder in Parallel.For

我创建了一个结构,比如 AStruct,并覆盖了它的 ToString() 方法。然后我写了一个平行于 return 一些 AStruct 并将它们放在一个列表中所以我可以使用 StreamWriter 来输出它们,比如

StreamWriter sw = new StreamWriter(@"ABC.txt");
StringBuilder sb = new StringBuilder();
List<AStruct> AList = new List<AStruct>();

Parallel.For(0,10,i =>                          // generate 10 AStruct(s)
{
   AList.Add(DoSomethingThatReturnsAStruct);
});

for(int i =0; i< AList.Count();i++)             //put in a StringBuilder
{
   sb.AppendLine(AList[i].ToString());
}
sw.Write(sb.ToString());
sw.Close();

问题是输出文件只打印了AList的7/8行,而AList实际上得到了所有的10个元素。我想知道这是否与 StringBuilder 的线程安全有关。有人可以解释为什么不是所有行都输出吗?

在你上面的代码中,StringBuilder 的实例永远不会被 main 线程(或创建 sw 的任何线程)以外的任何东西 accessed/modified 所以 [ StringBuilder 的 =36=] 不相关 但是 你有一个更大的错误,在处理多个线程时你应该永远记住它。

Never ever modify a shared resource by multiple threads unless that resource is thread-safe

您正在从不同线程更新 AList,因此要么使用 lock 来同步访问,要么使用 thread-safe 集合,例如ConcurrentQueue(保证订单)或 ConcurrentBag(不保证订单)

您还将 9 个条目添加到 AList 而不是 10.

最后是您修改后的代码,它产生了预期的结果。

var sw = new StreamWriter(@"ABC.txt");
try
{        
    var AList = new List<AStruct>();

    var locker = new object();
    Parallel.For(0, 10, i =>                          // generate 10 AStruct(s)
    {
        lock (locker) { AList.Add(new AStruct()); }
    });

    var sb = new StringBuilder();
    for (int i = 0; i < AList.Count; i++)             //put in a StringBuilder
    {
        sb.AppendLine(AList[i].ToString());
    }
    sw.Write(sb.ToString());
} finally
{
    sw.Close();
}