我做了一些实验性的Plinq查询,我不确定结果是否会被破坏。
这是3种不同的方法,它提供了相同的结果:
// unitTask is typeof Task<List<SomeEntity>>
//sequential version PLINQ
Console.WriteLine(unitTask.Result.Take(10)
.Aggregate(new StringBuilder(),
(text, current) => text.AppendFormat("@{0}sa{1}",
current.FullName.Substring(0, 3),
current.FullName.Substring(4)))
.ToString());
//parallel version PLINQ
Console.WriteLine(unitTask.Result.Take(10).AsParallel()
.Aggregate(new StringBuilder(),
(text, current) => text.AppendFormat("@{0}sa{1}",
current.FullName.Substring(0, 3),
current.FullName.Substring(4)))
.ToString());
//parallel version foreach with Partitioner
var output = new StringBuilder();
Parallel.ForEach(Partitioner.Create(unitTask.Result.Take(10)), r =>
{
//Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
output.AppendFormat("@{0}sa{1}", r.FullName.Substring(0, 3),
r.FullName.Substring(4));
});
Console.WriteLine(output.ToString());
我的问题是:
我可以在PLINQ中使用StringBuilder
吗?
正如我所知,由于append方法不是线程安全的。
或者在这种情况下是否以顺序模式运行?
Parallel.Foreach在不同的线程中运行查询,但结果与顺序Plinq相同。
是偶然的,还是聪明的并使用了一些同步?
答案 0 :(得分:1)
此版本不使用PLINQ,它使用标准LINQ,因此它是安全的。
此版本使用不能安全并行化的Aggregate()
重载,因此它也将在单个线程上执行。这意味着它是安全的,但它也不会比顺序版本更快。
要真正利用PLINQ,您需要使用实际可以并行执行的another overload of Aggregate()
。在您的情况下,这意味着每个线程都有一个单独的StringBuilder
,然后将所有StringBuilder
合并为一个。类似的东西:
input.AsParallel().Aggregate(
() => new StringBuilder(),
(text, current) => text.AppendFormat("@{0}", current.FullName),
(text1, text2) => text1.Append(text2),
text => text.ToString())
这假设您不关心最终字符串中元素的顺序。如果这样做,此代码将无法正常运行。
此代码修改多个线程中的相同StringBuilder
对象。 StringBuilder
不是线程安全的,因此此代码不安全。
答案 1 :(得分:0)
这是偶然的,可能是因为代码没有那么多并且可能在单个线程上运行。您的所有调用都会unitTask.Result
阻塞,直到unitTask
结束。所有片段实际上只对依次生成的10个实体起作用,因此没有足够的数据来证明并行执行
这三个片段做了不同的事情:
Aggregate
的调用也会收集所有工作人员的结果并按顺序处理它们以创建最终结果。