Expand on Eric Petroleum's answer .
If we rewrite the program as follows (the behavior is identical, but avoiding the lambda function makes it easier to read disassembly), we can decompose it and see what it really means to โcache the field value in Registerโ
class Foo { public bool Complete; // { get; set; } } class Program { static Foo foo = new Foo(); static void ThreadProc() { bool toggle = false; while (!foo.Complete) toggle = !toggle; Console.WriteLine("Thread done"); } static void Main() { var t = new Thread(ThreadProc); t.Start(); Thread.Sleep(1000); foo.Complete = true; t.Join(); } }
We get the following behavior:
Foo.Complete is a Field | Foo.Complete is a Property x86-RELEASE | loops forever | completes x64-RELEASE | completes | completes
in x86-release, the CLR JIT compiles while (! foo.Complete) into this code:
Completed field:
004f0153 a1f01f2f03 mov eax,dword ptr ds:[032F1FF0h]
The last two lines are the problem. If eax is zero, then it will just sit there in an infinite loop saying โEAX zero?โ Without any code ever changing the value of eax!
Completed property:
00220155 a1f01f3a03 mov eax,dword ptr ds:[033A1FF0h]
It really looks like a nicer code. While the JIT has built in the getter property (otherwise you will see some call
statements going to other functions), in some simple code, to read the Complete
field directly, because it is not allowed to cache the variable when it generates a loop, it repeatedly reads memory over and over, and not just reads caselessly
in x64-release, the 64-bit CLR JIT compiles while (! foo.Complete) into this code
Completed field:
00140245 48b8d82f961200000000 mov rax,12962FD8h
Property completed
00140250 48b8d82fe11200000000 mov rax,12E12FD8h
The 64-bit JIT does the same for both properties and fields, unless the field "expands" the first iteration of the loop - this essentially puts it if(foo.Complete) { jump past the loop code }
.
In both cases, it does a similar thing for the x86 JIT when working with the property:
- It inserts a method into a direct memory read - It does not cache it and rereads the value each time
I am not sure that in a 64-bit CLR it is not allowed to cache the value of a field in a register, as 32-bit does, but if it is, it does not interfere. Perhaps it will be in the future?
In any case, this illustrates how the behavior is platform dependent and can be changed. Hope this helps :-)