0

Today I accidentally stumbled upon a MemoryBarrier and realized that I do not fully understand the work of the CPU with RAM. The search did not give me an unambiguous answer to the question, so I decided to ask a new question.

There are two arrays:

int[] dataStates; //default items value is 0
MyStruct[] data; //MyStruct is a struct with several fields

There are two threads (ThreadA, ThreadB)

ThreadA performs the following code:

data[index] = ... //new value
//change to "1" after setting data
Interlocked.Exchange(ref dataStates[index], 1);

ThreadB code:

//wait for "1" in array item and replace to "2"
while(Interlocked.CompareExchange(ref dataStates[index], 2, 1) != 1)
{
     Thread.Sleep(0);
}

//read actual data
var value = data[index];

Is it possible that the Thread will read the data from the data[index] and they will be obsolete? By the word obsolete, I mean that the data received from the array will not match the data that was set before the Interlocked.Exchange call.

In general, I try data transfer between threads in the most productive way. Do use this approach (without locks) is it appropriate or are there more acceptable approaches?

  • Not familiar with Interlocked class. Normally do this with a Singleton. But your Thread.Sleep(0) is a problem since it does not force a context change. See: https://stackoverflow.com/a/3257751/2245849. – sthames42 Mar 05 '18 at 17:11

2 Answers2

0

I don't know if this is the solution but as far as I understood your sample you could do the following:

var queue = new ConcurrentQueue<DateTime>();
var tcs = new CancellationTokenSource();
var token = tcs.Token;
Task.Run(async () => {
    for (var i = 0; i < 2; i++) 
    {
        queue.Enqueue(DateTime.Now);
        await Task.Delay(2000);
    }
    tcs.Cancel();
}, token);
Task.Run(() => {
    while (!token.IsCancellationRequested) 
    {
        if (queue.Any())
        {
            DateTime result;
            queue.TryDequeue(out result);
            Console.WriteLine($"Received {result}...");
        }
    }
}, token).ContinueWith(t =>
{
    Console.WriteLine("Stop");
});
Console.ReadLine();
tcs.Cancel();

You will need some namespace-imports:

using System.Threading;
using System.Threading.Tasks
using System.Collections.Concurrent;

This is a complete different apporach avodiding manual sync between threads. I used DateTime instead of MyStruct.

Alexander Schmidt
  • 5,631
  • 4
  • 39
  • 79
0

From what I read in the reference to the methods Exchange and CompareExchange they do not imply a memory barrier. Hence, writing the value to data[index] may be swapped with the interlocked setting of dataStates[index], which means that the second thread may actually read invalid data.

I agree with sprinter252 that there is probably a better way to implement this. Isn't it a normal producer-consumer problem? This is solvable with semaphores or it can be rewritten to use a task queue as in sprinter's answer.

Kristof U.
  • 1,263
  • 10
  • 17