Microsoft's documention of Parallel.For包含以下方法:
http://www.code-stuff.net
http://www.code-stuff.net/HttpUtility
http://www.code-stuff.net/HttpUtility/minifyCSS
http://www.code-stuff.net/HttpUtility/stringify_json
在此方法中,可能有多个线程从static void MultiplyMatricesParallel(double[,] matA, double[,] matB, double[,] result)
{
int matACols = matA.GetLength(1);
int matBCols = matB.GetLength(1);
int matARows = matA.GetLength(0);
// A basic matrix multiplication.
// Parallelize the outer loop to partition the source array by rows.
Parallel.For(0, matARows, i =>
{
for (int j = 0; j < matBCols; j++)
{
double temp = 0;
for (int k = 0; k < matACols; k++)
{
temp += matA[i, k] * matB[k, j];
}
result[i, j] = temp;
}
}); // Parallel.For
}
和matA
读取值,这些值都是在调用线程上创建和初始化的,并且可能有多个线程将值写入matB
,稍后由调用线程读取。在传递给result
的lambda中,没有显式锁定数组的读写。因为这个例子来自微软,我认为它是线程安全的,但我试图了解幕后发生的事情,以使其成为线程安全的。
就我所理解的情况以及我在SO上提出的其他问题(例如this one)而言,我需要几个记忆障碍才能使这一切顺利进行。那些是:
Parallel.For
和matA
后,调用线程上的内存障碍,matB
和matA
的值之前,每个非调用线程上都有内存屏障,matB
和result
读取值之前调用线程上的内存屏障。我是否理解正确?
如果是的话,result
会以某种方式完成所有这些吗?我在参考源中挖掘但是在the code之后遇到了麻烦。我没有看到任何Parallel.For
阻止或lock
来电。
答案 0 :(得分:6)
由于数组已经创建,因此写入或读取数组不会导致任何调整大小。此外,代码本身也阻止了在数组中读/写相同的位置。
底线是代码总是可以计算在数组中读写的位置,并且这些调用永远不会相互交叉。因此,它是线程安全的。
答案 1 :(得分:6)
您正在寻找的内存障碍位于任务计划程序中。
ParallelFor将工作分解为任务,并且工作窃取调度程序执行这些任务。工作窃取调度程序所需的最小内存障碍是:
答案 2 :(得分:2)
在线程内部(实际上:任务)访问matA和matB是只读的,结果是只写的。
并行读取本质上是线程安全的,写入是线程安全的,因为i
变量对于每个任务都是唯一的。
在这段代码中不需要内存屏障(除了整个Parallel.For之前/之后的其他代码,但可以假设这些代码)。
为您编号项目,
1)和4)由Parallel.For()所暗示
根本不需要2)和3)。
答案 3 :(得分:2)
我认为你对记忆障碍的想法印象深刻,但我真的无法理解你的担忧。我们来看看您调查过的代码:
启动3个数组并使用主线程中的值填充。因此,就像为变量赋值并调用方法一样--CLR确保您的方法获得参数的新值。如果初始化是在后台完成的,那么可能可能会占用一席之地,而某些其他线程则同时执行。在这种情况下,你是对的,你需要一些同步结构,内存屏障或lock
语句或其他技术。
并行执行的代码获取从0
到matARows
的所有值,并为它们创建任务数组。您需要了解两种不同的方法来并行化代码:按操作和按数据。就在这里,我们有多行,它们具有相同的lambda操作。 temp
变量的赋值不是共享的,因此它们是线程安全的,不需要内存屏障,因为没有旧值和新值。同样,首先,如果其他一些线程更新初始矩阵,则需要在此处进行同步构造。
Parallel.For
确保完成所有任务(运行完成,取消或出现故障),直到它进入下一个语句,因此循环内的代码将作为普通方法执行。你为什么不在这里需要障碍?因为所有的写操作都是在不同的行上完成的,并且它们之间没有交集,所以它是数据并行性。但是,与其他情况一样,如果某些循环迭代中的新值需要其他线程,则仍需要同步。所以这段代码是线程安全的,因为它在数据上几何并行,并且不会产生竞争条件。
这个例子很简单,真正的算法一般需要更复杂的逻辑。您可以研究各种方法来确保代码是线程安全的,而无需使用代码同步来实现无锁。