使用 Parallel.ForEach() 线程安全吗?
本质上,我正在处理这个:
var data = input.AsParallel();
List<String> output = new List<String>();
Parallel.ForEach<String>(data, line => {
String outputLine = "";
// ** Do something with "line" and store result in "outputLine" **
// Additionally, there are some this.Invoke statements for updating UI
output.Add(outputLine);
});
输入是一个 List
对象。 ForEach()
语句对每个值进行一些处理,更新 UI,并将结果添加到 output
List
中。这有什么本质上的错误吗?
注释:
- 输出顺序不重要
更新:
根据我收到的反馈,我添加了手动锁定
到 output.Add
语句,以及 UI 更新代码。
Essentially, I am working with this:
var data = input.AsParallel();
List<String> output = new List<String>();
Parallel.ForEach<String>(data, line => {
String outputLine = "";
// ** Do something with "line" and store result in "outputLine" **
// Additionally, there are some this.Invoke statements for updating UI
output.Add(outputLine);
});
Input is a List<String>
object. The ForEach()
statement does some processing on each value, updates the UI, and adds the result to the output
List
. Is there anything inherently wrong with this?
Notes:
- Output order is unimportant
Update:
Based on feedback I've gotten, I've added a manual lock
to the output.Add
statement, as well as to the UI updating code.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
是的;
List
不是线程安全的,因此从任意线程(很可能同时)向其添加临时内容是注定的。您应该使用线程安全列表,或者手动添加锁定。或者也许有一个Parallel.ToList
。另外,如果重要的话:将不保证插入顺序。
不过,这个版本是安全的:
这里我们使用
index
来更新每个并行调用的不同数组索引。Yes;
List<T>
is not thread safe, so adding to it ad-hoc from arbitrary threads (quite possibly at the same time) is doomed. You should use a thread-safe list instead, or add locking manually. Or maybe there is aParallel.ToList
.Also, if it matters: insertion order will not be guaranteed.
This version is safe, though:
here we are using
index
to update a different array index per parallel call.是的,一切。这些都不安全。列表在多个线程上并发更新并不安全,并且您无法从 UI 线程以外的任何线程更新 UI。
Yes, everything. None of this is safe. Lists are not safe for updating on multiple threads concurrently, and you can't update the UI from any thread other than the UI thread.
文档介绍了以下关于
List< 的线程安全性;T>
:因此,output.Add(outputLine) 不是线程安全的,您需要自己确保线程安全,例如,将添加操作包装在锁中声明。
The documentation says the following about the thread safety of
List<T>
:Thus,
output.Add(outputLine)
is not thread-safe and you need to ensure thread safety yourself, for example, by wrapping the add operation in alock
statement.当您想要并行操作的结果时,PLINQ 比 < 更方便代码>并行类。您一开始就将
input
转换为ParallelQuery
:...但是随后您将
data
提供给Parallel.ForEach
,这将其视为标准IEnumerable
。所以AsParallel()
被浪费了。它不提供任何并行化,仅提供开销。以下是使用 PLINQ 的正确方法:您应该记住的一些差异:
Parallel
在ThreadPool
默认情况下,但它是 可配置。 PLINQ 专门使用ThreadPool
。Parallel
具有无限的并行性(它使用ThreadPool
的所有可用线程)。默认情况下,PLINQ 最多使用Environment.ProcessorCount< /code>
线程。
关于结果的顺序,PLINQ 默认情况下不保留顺序。如果您想保留顺序,可以附加
AsOrdered
运算符。When you want the results of a parallel operation, the PLINQ is more convenient than the
Parallel
class. You started well by converting yourinput
to aParallelQuery<T>
:...but then you fed the
data
to theParallel.ForEach
, which treats it as a standardIEnumerable<T>
. So theAsParallel()
was wasted. It didn't provide any parallelization, only overhead. Here is the correct way to use PLINQ:A few differences that you should have in mind:
Parallel
runs the code on theThreadPool
by default, but it's configurable. The PLINQ uses exclusively theThreadPool
.Parallel
by default has unlimited parallelism (it uses all the available threads of theThreadPool
). The PLINQ uses by default at mostEnvironment.ProcessorCount
threads.Regarding the order of the results, PLINQ doesn't preserve the order by default. In case you want to preserve the order, you can attach the
AsOrdered
operator.