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 确保每个操作都是自动发生的。
我正在使用遗传算法构建优化程序。我使用 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 确保每个操作都是自动发生的。