我正在使用遗传算法构建优化程序。我使用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);子句解决了意外的结果,但是当我与循环的正常比较时,这个操作导致大约10倍的时间。
答案 0 :(得分:5)
您可以使用最简单的Interlocked.Increment(int32)
方法。
使用Parallel.For
将创建多个线程,这些线程将执行相同的lambda表达式;在这种情况下,它只是j++
。
j++
将编译为类似j = j + 1
的内容,这是一个读写操作。这可能会导致不必要的行为。
说j = 50
。
线程1正在执行j++
的读取,它将获得50并将其加1。在该线程完成对j
的写操作之前,另一个线程执行读操作并从j
读取50,然后第一个线程完成对j
的写操作,使其成为51但是第二个线程线程仍然有50个内存作为j
的值,并将其加1并再次将51写回j
。
使用Interlocked
类可确保每个操作都以原子方式进行。
答案 1 :(得分: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();
}
}
答案 2 :(得分:3)
Parallel.For
等待所有迭代完成。您在变量中看到意外值的原因是不同的 - 这是预期的。
基本上,Parallel.For
将迭代调度到多个线程(如您所料)。但是,如果没有某种保护机制,多个线程不能共享相同的可写内存 - 如果有,则会有data race,结果在逻辑上是不确定的。这适用于所有编程语言,这是多线程的基本警告。
根据您的使用情况,您可以使用多种防护装置。他们工作的基本方式是atomic operations,您可以通过Interlocked
帮助程序类访问它们。更高级别的守卫包括Monitor
类,相关lock
language construct和类ReaderWriterLock
(及其兄弟姐妹)等类。