-2

before I start I just want to acknowledge that I am aware there are questions about this before that I've linked below:

Why do I need a memory barrier?

Why we need Thread.MemoryBarrier()?

That said... I've read both and still don't quite understand what's going on from the basic level.

class Foo
{
  int _answer;
  bool _complete;

  void A()
  {
    _answer = 123;
    Thread.MemoryBarrier();    // Barrier 1
    _complete = true;
    Thread.MemoryBarrier();    // Barrier 2
  }

  void B()
  {
    Thread.MemoryBarrier();    // Barrier 3
    if (_complete)
    {
      Thread.MemoryBarrier();       // Barrier 4
      Console.WriteLine (_answer);
    }
  }
}

This code snippet is taken from C# 4.0 in a Nutshell. Currently, I understand the problem without memory barriers is that there's a possibility that B will run before A and B will print nothing because _complete could be evaluated as false.

The "barriers" in each function are completely separate with each other and it's not like the barriers are ordered... Thread.MemoryBarrier(1) or anything so the compiler doesn't know A should go before B.

Could someone clear this up for me? Thanks

EDIT: I think I'm confused about how instruction ordering works... but I'm so confused about the topic that I'm not even sure how to phrase my question appropriately.

Community
  • 1
  • 1
Luke Xu
  • 2,302
  • 3
  • 19
  • 43

1 Answers1

2

Currently, I understand the problem without memory barriers is that there's a possibility that B will run before A and B will print nothing because _complete could be evaluated as false.

No, the problem is in compiler, jitter or CPU instruction reordering. It can be case, when some of them could reorder

_answer = 123;
_complete = true;

instructions for some optimization as form point of view single threaded application there is no matter order of them.

Now suppose they are reordered as

_complete = true;
_answer = 123;

now:

  • Thread 1 set _complete = true
  • Thread 2 get _complete
    • evaluate if condition
    • get _answer (which is 0)
    • Console.WriteLine(_answer) ->0
  • Thread 1 set _answer = 123

The code logic broken.

Hamlet Hakobyan
  • 32,965
  • 6
  • 52
  • 68
  • OMG! I completely get it now, I can't believe it was that simple. I was completely over thinking it. I have 1 more follow up question if you don't mind. – Luke Xu Jan 17 '16 at 19:28
  • Which part of code is Thread.MemoryBarrier(); controlling? For example.... the first call Thread.MemoryBarrier() ... What what piece of code is that "stopping" from being reorganized? – Luke Xu Jan 17 '16 at 19:30
  • First memory barrier set full fence and disallows moving `_answer = 123;` next to the `_complete = true;` and vise versa and second barrier releases the cash line of CPU and ensures that the `_answer` and `_complete` are fresh. – Hamlet Hakobyan Jan 17 '16 at 19:36