似乎ConcurrentBag不是线程安全的



我写了一个程序,一个列表生成器方法返回IEnumerable的字符串,包括很多字符串(100万个项目(,我把它存储在字符串列表中,然后它附加Parallel.Foreach的StringBuilder实例中的所有项目。然后我打印了stringBuilderInstance.Length

问题是它小于 1000000 . 经过一番谷歌搜索,我意识到 列表集合不是线程安全的 ,这导致了这个问题。 所以我想到了2个解决方案:

1( 使用

2( 使用并发包

当我使用锁时,还可以,长度是 1 milion,但是:

当我使用字符串的并发包时,长度比我预期的要少!

这个问题的根本原因是什么?

列表创建者方法:

public static List<string> CreateList()
{
List<string> result = new List<string>();
for (int i = 0; i < 1000000; i++)
{
result.Add(1.ToString());
}
return result;
}

使用 ConcurrentBag :

public static void DoWithParallel_ThreadSafe()
{
ConcurrentBag<string> listOfString = new ConcurrentBag<string>(CreateList());
StringBuilder a = new StringBuilder();
Action<string> appender = (number) =>
{
a.Append(number);
};
Parallel.ForEach(listOfString, appender);
Console.WriteLine($"The string builder lenght : {a.Length}");
}

使用锁 :

public static void DoWithParallel_UnsafeThread_Lock()
{
List<string> listOfString = CreateList();
StringBuilder a = new StringBuilder();
Action<string> appender = (number) =>
{
lock (listOfString)
{
a.Append(number);
}
};
Parallel.ForEach(listOfString, appender);
Console.WriteLine($"The string builder lenght : {a.Length}");
}

主要:

static void Main(string[] args)
{
DoWithParallel_UnsafeThread_Lock();
DoWithParallel_ThreadSafe();
Console.ReadKey();
}

提前谢谢你。

StringBuilder不能从多个线程中变异,因此当您尝试这样做时代码不起作用的原因。 请注意,锁定毫无意义;只是不要首先创建多个线程来完成工作,因为工作不能从多个线程完成。 由于您永远不会从多个线程访问ConcurrentBag,因此使用它而不是List毫无意义。

StringBuilder不是线程安全的,这就是为什么一些Append()调用"丢失"的原因。因此,即使您的集合是线程安全的,您仍然需要锁。

(另外,请参阅 Servy 的答案,了解为什么根本不需要集合是线程安全的。

最新更新