Microsoft's documention of Parallel.For contains the following method:
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
}
In this method, potentially multiple threads read values from matA
and matB
, which were both created and initialized on the calling thread, and potentially multiple threads write values to result
, which is later read by the calling thread. Within the lambda passed to Parallel.For
, there is no explicit locking around the array reads and writes. Because this example comes from Microsoft, I assume it's thread-safe, but I'm trying to understand what's going on behind the scenes to make it thread-safe.
To the best of my understanding from what I've read and other questions I've asked on SO (for example this one), several memory barriers are needed to make this all work. Those are:
matA
and matB
,matA
and matB
,result
, andresult
.Have I understood this correctly?
If so, does Parallel.For
do all of that somehow? I went digging in the reference source but had trouble following the code. I didn't see any lock
blocks or MemoryBarrier
calls.
In computing, a memory barrier, also known as a membar, memory fence or fence instruction, is a type of barrier instruction that causes a central processing unit (CPU) or compiler to enforce an ordering constraint on memory operations issued before and after the barrier instruction.
volatile in most programming languages does not imply a real CPU read memory barrier but an order to the compiler not to optimize the reads via caching in a register. This means that the reading process/thread will get the value "eventually".
Memory barriers, or fences, are a set of processor instructions used to apply ordering limitations on memory operations.
The answer is: No. A function call is always a compiler barrier. But a function call is not guaranteed to be a memory barrier. It only is if the code in the called function contains a memory barrier.
Since the array is already created, writing or reading from it won't cause any resizes. Also, the code itself prevents reading/writing the same position in the array.
Bottom line is that the code always can calculate the position to read and write in the array and those calls never cross each other. Hence, it is thread-safe.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With