TPL没有使用Parallel.For和List <t> </t>产生正确的结果

时间:2013-09-02 05:32:24

标签: c# task-parallel-library .net-4.5

我有一个简单的程序,它返回低于某个数字的素数之和。

当我在不使用TPL的情况下运行该程序时,它会给出正确的结果,但是当我使用TPL运行程序时,它会得到错误的结果。

我无法理解这有什么问题。有人可以帮忙吗?

以下是代码:

static void Main(string[] args)
{
    var executionStartTime = DateTime.Now;
    Console.WriteLine(GetSumOfPrimesBelowviaTPL(2000000));
    Console.WriteLine("End Time: " + (DateTime.Now - executionStartTime).ToString("T"));
    var a = Console.ReadLine();
}

private static long GetSumOfPrimesBelow(int number)
{
    long sumOfPrimes = 0;
    for (int i = 2; i < number; i++)
    {
        if ((i == 2 || i % 2 != 0) && (i == 3 || i % 3 != 0) && IsPrime(i))
        {
            sumOfPrimes += i;
        }
    }
    return sumOfPrimes;
}

private static long GetSumOfPrimesBelowviaTPL(int number)
{
    long sumOfPrimes = 0;
    var primeNumbersList = new List<int>();
    Parallel.For(2, number, i =>
    {
        if ((i == 2 || i % 2 != 0) && (i == 3 || i % 3 != 0) && IsPrime(i))
        {
            primeNumbersList.Add(i);
        }
    });
    foreach (var item in primeNumbersList)
    {
        Console.WriteLine(item);
        sumOfPrimes += item;
    }
    return sumOfPrimes;
}

2 个答案:

答案 0 :(得分:1)

在你的Parallel.For中,你希望有这样的东西

lock (primeNumbersList)
{
      primeNumbersList.Add(i);
}

然后它会产生相同的结果。列表不是线程友好的,写入列表可能会导致麻烦。

另外我注意到,你的算法看起来不是很有效,为什么循环两次,如果你能在你的Prallel内计算你的总和。对于

答案 1 :(得分:1)

我认为你面临的是生产者 - 消费者问题。列表类型也不是线程安全的。

不确定这是否能解决您的问题所以请试一试并告诉我。

  1. 在您的GetSumOfPrimesBelowviaTPL方法中,而不是List使用BlockingCollection。所以你的primeNumbersList不是List类型而是BlockingCollection类型。这将确保线程安全。它也比自定义锁定结构更有效。

  2. 在foreach之前写下primeNumbersList.CompleteAdding()。这将解决您的生产者 - 消费者问题。

  3. 在foreach中,不要直接使用primeNumbersList,而是使用primeNumbersList.GetConsumingEnumerable()。

  4. 所以你的方法应该是这样的。

    private static long GetSumOfPrimesBelowviaTPL(int number)
    {
        long sumOfPrimes = 0;
        var primeNumbersList = new BlockingCollection<int>();
        Parallel.For(2, number, i =>
        {
            if ((i == 2 || i % 2 != 0) && (i == 3 || i % 3 != 0) && IsPrime(i))
            {
                primeNumbersList.Add(i);
            }
        });
    
        primeNumbersList.CompleteAdding();
    
        foreach (var item in primeNumbersList.GetConsumingEnumerable())
        {
            Console.WriteLine(item);
            sumOfPrimes += item;
        }
        return sumOfPrimes;
    }
    

    显然,您需要包含一些名称空间。我没有测试过这种方法,所以如果有任何编译错误,请原谅我。