并行函数在C#中返回不同的结果

时间:2016-02-17 18:54:27

标签: c# multithreading primes

我在Windows窗体C#应用程序中有这些代码:

private void button7_Click(object sender, EventArgs e)
{
    ThreadStart starter = delegate { thread_func(2, 1000000); };
    thread1_thread = new Thread(starter);
    starter = delegate { thread_func(1000000, 2000000); };
    thread2_thread = new Thread(starter);
    starter = delegate { thread_func(2000000, 3000000); };
    thread3_thread = new Thread(starter);
    starter = delegate { thread_func(3000000, 4000000); };
    thread4_thread = new Thread(starter);

    thread1_thread.Start();
    thread2_thread.Start();
    thread3_thread.Start();
    thread4_thread.Start();

}

void thread_func(decimal input1,decimal input2)
{
    for (; input1 < input2; input1++)
    {
        threadNumbers_list.Add(input1);
        if (input1 % 2 != 0)
        {
            if (isPrime_func(input1))
            {
                PrimeNumbers_decimal_list.Add(input1);
            }
        }
    }
}
public static Boolean isPrime_func(decimal number)
    {
        decimal boundary = (decimal)Math.Floor(Math.Sqrt((double)number));

        if (number == 1) return false;
        if (number == 2) return true;

        for (decimal i = 2; i <= boundary; ++i)
        {
            if (number % i == 0) return false;
        }

        return true;
    }

每次我点击该按钮,我都会得到不同的结果。我尝试了很多东西,但无法弄清楚为什么会这样。即使是较低的范围也会发生。例如,在100个数字的范围内,它总是给出相同的结果。 有一段时间我的列表计数达到283138,有时候是283131和其他近似数字。

另一个奇怪的是,当我评论检查偶数时,操作比这个模式花费的时间更短。怎么了?

1 个答案:

答案 0 :(得分:2)

当多个线程访问列表时,该列表必须是线程安全的,否则您将遇到很多问题。

.NET提供了thread-safe collectionsConcurrentQueue<T>之类的一些supports data parallelism via the Parallel class

附注:请考虑使用Tasks代替线程。此外,.NET框架http://codesheet.org/codesheet/wOHDXFju。考虑使用这样的类。

关于不检查数字是否均匀时的性能,我在本地进行了测试,得到了以下数字:

  • 当我不检查数字是否均匀时需要~76秒。
  • 检查数字是否均匀需要约66秒。

因此,这与您的测量值不符。这可能是由您测量的方式引起的。我用这样的Stopwatch来衡量:

//...

Stopwatch sw = Stopwatch.StartNew();

thread1_thread.Start();
thread2_thread.Start();
thread3_thread.Start();
thread4_thread.Start();

thread1_thread.Join();
thread2_thread.Join();
thread3_thread.Join();
thread4_thread.Join();

long result = sw.ElapsedMilliseconds;

//...

顺便说一句,你可以做的就是为你节省一些执行时间:

List<T>方法中的每个线程创建一个普通的thread_func实例,这样就不会出现多线程问题。然后在循环结束后,您可以从本地列表更新主列表。只更新主列表必须是线程安全的。在这种情况下,我希望主列表是正常的List<T>,并且您使用lock关键字来同步对它的访问,因为您只需要更新它4次(线程数)。