5

Why in this part of code complete is cached?

static void Main()
{
  bool complete = false; 
  var t = new Thread (() =>
  {
    bool toggle = false;
    while (!complete) toggle = !toggle;
  });
  t.Start();
  Thread.Sleep (1000);
  complete = true;
  t.Join();        // Blocks indefinitely
}

but in this part it isn't?

static void Main()
{
  bool complete = false;
  bool toggle = false; 
  var t = new Thread (() =>
  {
    while (!complete) toggle = !toggle;
  });
  t.Start();
  Thread.Sleep (1000);
  complete = true;
  t.Join();  
}
bas
  • 13,550
  • 20
  • 69
  • 146
Hamlet Hakobyan
  • 32,965
  • 6
  • 52
  • 68

3 Answers3

3

You are doing unsynchronized data sharing across threads. Sirens should now go off.

As I understand the memory model the JIT is allowed to read complete once and store it in a register. For that reason the update from Main never becomes visible.

As for fixing this, the simplest way is to wrap a lock around accesses to complete. You could also use Thread.VolatileRead and Thread.VolatileWrite.

usr
  • 168,620
  • 35
  • 240
  • 369
  • The question not about fixing. **Why** in first snippet `compare` is cached but in second isn't? – Hamlet Hakobyan Mar 01 '13 at 16:59
  • Ok, I missed that point. I guess this is because in the 2nd snippet toggle is a heap write while in the 1st it is a local variable write. Heap stores prevent certain reorderings. They prevent sometimes "caching" across them. Either this is formally guaranteed by the x86/.NET memory model or it is just a JIT optimizer quirk/property. – usr Mar 01 '13 at 17:07
3

Don't be sure caching will happen on all architectures as above, or even that it will always happen as above in multiple runs of the program.

It could be because in the second case the lambda is modifying the closure, while in first it is only accessing the closure. Of course this is only a wild guess.

More importantly though, there are no guarantees about when caching will be done, memory boundary only specifies when the thread will not use cached value.

Bottom line, you cannot rely on caching to occur.

almaz
  • 216
  • 3
  • 11
Miserable Variable
  • 28,432
  • 15
  • 72
  • 133
0

Both samples complete in my case, but you're lambda/delegate can be compile differently in different compilers, which could be the problem.

You delegate are accessing a modified closure, which can cause a bunch of problems and you have apparently found one. Take a look at the answer provided by Jon Skeet here, Access to Modified Closure, it is not exactly the same problem, but the reason described here works in you case too.

Community
  • 1
  • 1
Xharze
  • 2,703
  • 2
  • 17
  • 30