Parallel.For 不等待所有迭代

Parallel.For does not wait all iterations

我正在使用遗传算法构建优化程序。我使用 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(参考 j);子句解决了意想不到的结果,但是当我与普通的 for 循环相比时,这个操作导致大约多 10 倍的时间。

您对 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();
    }
} 

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

基本上,Parallel.For 将迭代分派给多个线程(如您所料)。但是,如果没有某种保护机制,多个线程不能共享相同的可写内存 - 如果它们共享,您将拥有 data race 并且结果在逻辑上是不确定的。这适用于所有编程语言,是多线程的基本警告。

根据您的用例,您可以设置多种保护措施。他们工作的基本方式是通过 atomic operations, which are accessible to you through the Interlocked helper class. Higher-level guards include the Monitor class, the related lock language construct and classes like ReaderWriterLock(及其兄弟姐妹)。

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

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

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

j = 50.

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

使用 Interlocked class 确保每个操作都是自动发生的。