0

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

当我在不使用 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;
}
4

2 回答 2

1

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

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

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

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

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

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

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;
}

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

于 2013-09-02T06:10:51.627 回答
1

在你的 Parallel.For 中,你想要这样的东西

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

然后它将产生相同的结果。列表不是线程友好的,像这样写到列表会引起麻烦。

我还注意到,你的算法似乎不是很有效,为什么循环两次,如果你可以在你的 Prallel.For 中计算你的总和

于 2013-09-02T06:03:45.557 回答