0

我正在使用遗传算法构建优化程序。我使用 Parallel.For 来减少时间。但它导致了一个与下面的代码相同的问题:

class Program
    {
        static void Main(string[] args)
        {
            int j=0;
            Parallel.For(0, 10000000, i =>
                {
                    j++;
                });
            Console.WriteLine(j);
            Console.ReadKey();
        }
    } 

每次我运行上面的程序时,它都会在 0 到 10000000 之间写入一个不同的 j 值。我猜它不会等待所有迭代完成。它传递到下一行。我应该如何解决这个问题?任何帮助将不胜感激。谢谢。

版本:Interlocked.Increment(ref j); 子句解决了意外的结果,但是当我与正常的 for 循环相比时,此操作会导致大约 10 倍的时间。

4

3 回答 3

5

您可以使用Interlocked.Increment(int32)可能最简单的方法。

UsingParallel.For将创建多个线程,这些线程将执行相同的 lambda 表达式;在这种情况下,它所做的只是j++.

j++将被编译成这样的东西j = j + 1,这是一个读写操作。这可能会导致不需要的行为。

这么说j = 50

线程 1 正在执行读取j++,它将获得 50 并将向其添加 1。在该线程可以完成对另一个线程的写入操作之前,j执行读取操作并从中读取 50,j然后第一个线程已完成写入操作使其变为j51,但第二个线程在内存中仍有 50 作为值j并将添加 1再次将 51 写回j.

使用Interlocked该类可确保每个操作都以原子方式发生。

于 2015-01-27T14:41:24.643 回答
3

Parallel.For 确实等待所有迭代完成。您在变量中看到意外值的原因是不同的 - 这是意料之中的。

基本上,Parallel.For将迭代分派给多个线程(如您所料)。但是,如果没有某种保护机制,多个线程不能共享相同的可写内存——如果它们这样做了,就会发生数据竞争,结果在逻辑上是不确定的。这适用于所有编程语言,它是多线程的基本警告。

根据您的用例,您可以设置多种防护措施。它们工作的基本方式是通过原子操作,您可以通过Interlocked帮助类访问这些操作。更高级别的守卫包括Monitor类、相关的lock语言结构和类ReaderWriterLock(及其兄弟)。

于 2015-01-27T14:40:58.607 回答
3

您的访问权限j未同步。请阅读有关多线程和同步的基础书籍或教程。

Parallel.For 确实等待所有迭代。

使用同步(从而避免使用并行):

class Program
{
    static void Main(string[] args)
    {
        object sync = new object;
        int j=0;
        Parallel.For(0, 10000000, i =>
            {
                lock(sync)
                {
                  j++;
                }
            });
        Console.WriteLine(j);
        Console.ReadKey();
    }
} 
于 2015-01-27T14:40:08.777 回答