none
Memory Barrier Question

    Question

  • I have a parallel algorithm that looks like this:

    Step 1: Mutate a bunch of global data
    Step 2: Call Thread.MemoryBarrier
    Step 3: Set a bunch of AutoResetEvents for stalled threads so they wake up and process the global data (treating global data as read-only)
    Step 5: Go back to Step 1 and repeat

    Do I need step #2? I was thinking that if step 3 was the creation of threads, there is an implicit memory barrier. But since step 3 is simply un-stalling existing threads (they are waiting on reset events), I would probably need one, correct?

    Tuesday, April 18, 2017 9:38 PM

Answers

  • No you don't need a memory barrier.

    You, however, should probably mark the field as volatile if you intend to change it so the compiler doesn't optimize it. Also I believe there was a change to the memory model way back in CLR 4 or thereabouts that helps ensure that volatile are not reordered in an invalid way. In general the optimizer can cache reads and reorder them. It simply cannot move reads before writes. But when other threads are changing the value then this breaks. I don't believe that to be the case in the simple example you gave but if this code were to be maintained over time it becomes a distinct possibility.

    Wednesday, April 19, 2017 8:26 PM
    Moderator

All replies

  • Hi Elliott Prechter,

    Thank you for posting here.

    You use the Thread.MemoryBarrier to do the synchronization. You could use the lock or monitor to do the synchronization directory.

    I hope this would be helpful.

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, April 19, 2017 8:14 AM
    Moderator
  • Thanks, but I don't understand why an explicit MemoryBarrier would be required here. Doesn't the AutoResetEvent.Set() cause an implicit memory barrier in the main thread, and AutoResetEvent.WaitOne()s cause implicit memory barriers in the slave threads? So it should be safe to remove Step 2 in my example?

    The global data is treated as readonly inside the slave threads, so it doesn't need locking. I just want to be sure that the global data's state is properly reflected in the cache-lines of all processors when each parallel processing step occurs (Step 3).

    I just want to be sure I understand these synchronization constructs correctly, thanks!

    Wednesday, April 19, 2017 2:04 PM
  • In general you don't need to use a memory barrier (I cannot think of a time I needed one). If you're mucking with data shared across multiple threads you should be using locks. Contrary to what you might think, even accessing read only data requires locks, depending upon how you're using it. For example if your thread is going to enumerate the data then, without a lock, you'll occasionally see an exception because the data can change while enumerating (not a good thing).

    Assuming all the threads are referencing the same underlying field (or a variable that points to the same field) then memory barriers shouldn't be needed. You'd just need it if you were reassigning a field across threads. Then you'd want a barrier to ensure all the processors were using the same value (since they may get out of sync across assignments).

    Wednesday, April 19, 2017 6:11 PM
    Moderator
  • You'd just need it if you were reassigning a field across threads. Then you'd want a barrier to ensure all the processors were using the same value (since they may get out of sync across assignments).

    Ok thanks, I put together this code example to illustrate the question more clearly:

    The following code reassigns "_shared" by the main thread, and it's used in a read-only fashion by the slave thread. The main thread never mutates the shared value while the slave thread is actually executing, hence why I don't need to lock on the shared data in this particular case.

    My question is: Do I need to put in an explicit memory barrier before the call to _start.Set() to ensure that after the main thread mutates "_shared" the slave thread sees the updated value? I think I might not need to put one in explicitly because _start.Set() already implies a memory barrier, correct?

    TL;DR; is there a synchronization bug hidden in this example code?
    using System;
    using System.Threading;
    namespace test {
    
    class MainClass {
    	private static string _shared;
    	private static Thread _slave = new Thread( new ThreadStart( Slave ) );
    	private static AutoResetEvent _start = new AutoResetEvent( false );
    	private static CountdownEvent _done = new CountdownEvent( 1 );
    
    	public static void Slave() {
    		while( _start.WaitOne() ) {
    			// use shared data without mutating it
    			Console.WriteLine( _shared );
    			_done.Signal();
    		}
    	}
    
    	public static void Main( string[] args ) {
    		_slave.Start();
    		for( var i = 0; i < 100; i++ ) {
    			// mutate shared data -> do I need a memory barrier after the mutation to ensure the slave thread uses the new value of '_shared'? I think _start.Set() implies one so I should be OK.
    			_shared = i.ToString();
    			_start.Set();
    			_done.Wait();
    			_done.Reset();
    		}
    	}
    }}




    Wednesday, April 19, 2017 7:46 PM
  • No you don't need a memory barrier.

    You, however, should probably mark the field as volatile if you intend to change it so the compiler doesn't optimize it. Also I believe there was a change to the memory model way back in CLR 4 or thereabouts that helps ensure that volatile are not reordered in an invalid way. In general the optimizer can cache reads and reorder them. It simply cannot move reads before writes. But when other threads are changing the value then this breaks. I don't believe that to be the case in the simple example you gave but if this code were to be maintained over time it becomes a distinct possibility.

    Wednesday, April 19, 2017 8:26 PM
    Moderator