使用StringBuilder进行Plinq Aggregate扩展

时间:2013-12-09 11:23:49

标签: c# stringbuilder plinq

我做了一些实验性的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相同。

是偶然的,还是聪明的并使用了一些同步?

2 个答案:

答案 0 :(得分:1)

  1. 此版本不使用PLINQ,它使用标准LINQ,因此它是安全的。

  2. 此版本使用不能安全并行化的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())
    

    这假设您不关心最终字符串中元素的顺序。如果这样做,此代码将无法正常运行。

  3. 此代码修改多个线程中的相同StringBuilder对象。 StringBuilder不是线程安全的,因此此代码不安全。

答案 1 :(得分:0)

这是偶然的,可能是因为代码没有那么多并且可能在单个线程上运行。您的所有调用都会unitTask.Result阻塞,直到unitTask结束。所有片段实际上只对依次生成的10个实体起作用,因此没有足够的数据来证明并行执行

这三个片段做了不同的事情:

  1. 第一个代码段只是按顺序处理10个代码段的列表。
  2. PLINQ版本列出了10个实体,但没有并行执行任何操作。即使这样做,对Aggregate的调用也会收集所有工作人员的结果并按顺序处理它们以创建最终结果。
  3. 第三个片段可以显示并行行为,因为它并行执行一个动作块。同样,结果数量太小,只使用一个线程