1

我试图想出一种方法来以最快的方式加速组合列表中包含的大量对象。希望利用 PLINQ 我尝试过,但这不是线程安全的解决方案。我在 4.0 和 4.5 的 VS2010 和 VS11Beta 中进行了测试。这是我的示例应用程序。如果您在 1-500 之间更改 BlowUp(),它通常会起作用。500 年后车轮脱离轨道。它会在多个地方失败。有谁知道解决这个问题的最快方法?(多维数组 + PLINQ?)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PLinqBlowsUp
{
class Program
{
    static void Main(string[] args)
    {
        BlowUp(5000);
    }

    private static void BlowUp(int blowupNum)
    {
        try
        {
            var theExistingMasterListOfAllRowsOfData = new List<List<KeyValuePair<string, dynamic>>>();

            //Add some test data
            Enumerable.Range(0, blowupNum).AsParallel().ForAll(row => theExistingMasterListOfAllRowsOfData.Add(AddRowToMasterList(row)));


            var aNewRowOfData = new List<KeyValuePair<string, dynamic>>();
            //Add some test data
            var column = new KeyValuePair<string, dynamic>("Title", "MyTitle");
            aNewRowOfData.Add(column);

            var anotherNewRowOfData = new List<KeyValuePair<string, dynamic>>();
            //Add some test data
            var columnA = new KeyValuePair<string, dynamic>("Date", DateTime.Now);
            var columnB = new KeyValuePair<string, dynamic>("ImportantColumn", "ImportantData");
            var columnC = new KeyValuePair<string, dynamic>("VeryImportantColumn", "VeryImportantData");
            anotherNewRowOfData.Add(columnA);
            anotherNewRowOfData.Add(columnB);
            anotherNewRowOfData.Add(columnC);

            //Now the Problem
            aNewRowOfData.AsParallel().ForAll(anrod => theExistingMasterListOfAllRowsOfData.ForEach(temloarod => temloarod.Add(anrod)));
            anotherNewRowOfData.AsParallel().ForAll(anrod => theExistingMasterListOfAllRowsOfData.ForEach(temloarod => temloarod.Add(anrod)));

            //Test for number
            foreach (var masterRow in theExistingMasterListOfAllRowsOfData)
            {
                if (masterRow.Count != 7)
                    throw new Exception("BLOW UP!!!");
            }
        }
        catch (AggregateException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    private static List<KeyValuePair<string, dynamic>> AddRowToMasterList(int row)
    {
        var columnA = new KeyValuePair<string, dynamic>("FirstName", "John" + row.ToString());
        var columnB = new KeyValuePair<string, dynamic>("LastName", "Smith" + row.ToString());
        var columnC = new KeyValuePair<string, dynamic>("Ssn", 123456789 + (row*10));

        var list = new List<KeyValuePair<string, dynamic>>();
        list.Add(columnA);
        list.Add(columnB);
        list.Add(columnC);
        return list;
    }
}
}
4

3 回答 3

4

这与 PLinq 无关——将项目添加到 aList<T>根本不是线程安全的。一个会降低性能的可行解决方案是引入锁定。相反,您通常想要做的是投影到一个集合作为您的 PLinq 语句的结果 - 像您所做的那样引入副作用通常不符合 Linq / 函数式编程的精神,您可能会遇到麻烦(如你做了)。

于 2012-05-22T17:11:57.177 回答
3

我看到两个问题。

  • 您正在从多个线程调用Add该实例,而没有任何尝试同步对它的访问。theExistingMasterListOfAllRowsOfData
  • 您正在从多个线程调用Add单个项目,而没有任何尝试同步它们。List<KeyValuePair<string, dynamic>>

您可以使用lock来保护Add方法或使用 aConcurrentBag代替。但是,这些选项都不是那么好。这里的问题是这种操作不能很好地并行化,因为所有线程最终都会竞争同一个锁。我高度怀疑,即使是低锁ConcurrentBag最终也会比您刚刚在 PLINQ 上踢并在主线程上完成所有操作时要慢。

于 2012-05-22T18:24:06.530 回答
2

PLinq 不能替代编写线程安全代码。您的访问theExistingMasterListOfAllRowsOfData不是线程安全的,因为线程池中的所有线程都在访问它。您可以尝试锁定它,这为我解决了问题:

Enumerable.Range(0, blowupNum).AsParallel().ForAll(row => {
    lock (theExistingMasterListOfAllRowsOfData) {                 
        theExistingMasterListOfAllRowsOfData.Add(AddRowToMasterList(row));
    }
});

但是,锁定可能不是您所追求的,因为这会引入瓶颈。

于 2012-05-22T17:11:33.303 回答