Để mở rộng trên Eric Petroelje's answer.
Nếu chúng ta viết lại chương trình như sau (hành vi giống hệt nhau, nhưng tránh hàm lambda giúp dễ dàng đọc được giải mã), chúng ta có thể giải thích nó và xem nó thực sự có ý nghĩa gì để "lưu trữ giá trị của trường một đăng ký"
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();
}
}
Chúng tôi nhận được các hành vi sau đây:
Foo.Complete is a Field | Foo.Complete is a Property
x86-RELEASE | loops forever | completes
x64-RELEASE | completes | completes
trong x86 phát hành, JIT CLR biên dịch trong khi (foo.Complete!) vào mã này:
Hoàn thành là một trường:
004f0153 a1f01f2f03 mov eax,dword ptr ds:[032F1FF0h] # Put a pointer to the Foo object in EAX
004f0158 0fb64004 movzx eax,byte ptr [eax+4] # Put the value pointed to by [EAX+4] into EAX (this basically puts the value of .Complete into EAX)
004f015c 85c0 test eax,eax # Is EAX zero? (is .Complete false?)
004f015e 7504 jne 004f0164 # If it is not, exit the loop
# start of loop
004f0160 85c0 test eax,eax # Is EAX zero? (is .Complete false?)
004f0162 74fc je 004f0160 # If it is, goto start of loop
Hai dòng cuối cùng là vấn đề. Nếu eax bằng không, thì nó sẽ chỉ ngồi đó trong một vòng lặp vô hạn nói "là EAX không?", mà không có bất kỳ mã nào thay đổi giá trị của eax!
Complete là một tài sản:
00220155 a1f01f3a03 mov eax,dword ptr ds:[033A1FF0h] # Put a pointer to the Foo object in EAX
0022015a 80780400 cmp byte ptr [eax+4],0 # Compare the value at [EAX+4] with zero (is .Complete false?)
0022015e 74f5 je 00220155 # If it is, goto 2 lines up
này thực sự trông như mã đẹp hơn. Trong khi JIT đã phác thảo thuộc tính getter (nếu không bạn sẽ thấy một số hướng dẫn call
chuyển sang các hàm khác) vào một số mã đơn giản để đọc trực tiếp trường Complete
, vì nó không được phép lưu biến, khi nó tạo vòng lặp, liên tục đọc bộ nhớ hơn và hơn nữa, thay vì chỉ pointlessly đọc thanh ghi
trong x64-phát hành, 64 bit CLR JIT biên dịch trong khi (! foo.Complete) vào mã này
Complete là một lĩnh vực :
00140245 48b8d82f961200000000 mov rax,12962FD8h # put 12E12FD8h into RAX. 12E12FD8h is a pointer-to-a-pointer in some .NET static object table
0014024f 488b00 mov rax,qword ptr [rax] # Follow the above pointer; puts a pointer to the Foo object in RAX
00140252 0fb64808 movzx ecx,byte ptr [rax+8] # Add 8 to the pointer to Foo object (it now points to the .Complete field) and put that value in ECX
00140256 85c9 test ecx,ecx # Is ECX zero ? (is the .Complete field false?)
00140258 751b jne 00140275 # If nonzero/true, exit the loop
0014025a 660f1f440000 nop word ptr [rax+rax] # Do nothing!
# start of loop
00140260 48b8d82f961200000000 mov rax,12962FD8h # put 12E12FD8h into RAX. 12E12FD8h is a pointer-to-a-pointer in some .NET static object table
0014026a 488b00 mov rax,qword ptr [rax] # Follow the above pointer; puts a pointer to the Foo object in RAX
0014026d 0fb64808 movzx ecx,byte ptr [rax+8] # Add 8 to the pointer to Foo object (it now points to the .Complete field) and put that value in ECX
00140271 85c9 test ecx,ecx # Is ECX Zero ? (is the .Complete field true?)
00140273 74eb je 00140260 # If zero/false, go to start of loop
Hoàn thành là tài sản
00140250 48b8d82fe11200000000 mov rax,12E12FD8h # put 12E12FD8h into RAX. 12E12FD8h is a pointer-to-a-pointer in some .NET static object table
0014025a 488b00 mov rax,qword ptr [rax] # Follow the above pointer; puts a pointer to the Foo object in RAX
0014025d 0fb64008 movzx eax,byte ptr [rax+8] # Add 8 to the pointer to Foo object (it now points to the .Complete field) and put that value in EAX
00140261 85c0 test eax,eax # Is EAX 0 ? (is the .Complete field false?)
00140263 74eb je 00140250 # If zero/false, go to the start
Phiên bản 64-bit JIT đang làm điều tương tự cho cả tài sản và các lĩnh vực, trừ khi đó là một lĩnh vực đó là "trải ra" phiên đầu tiên của vòng lặp - điều này về cơ bản đặt dấu if(foo.Complete) { jump past the loop code }
ở phía trước của nó đối với một số lý do.
Trong cả hai trường hợp, nó đang làm một điều tương tự như JIT x86 khi giao dịch với một tài sản:
- Nó inlines phương pháp để một bộ nhớ trực tiếp đọc - Nó không cache nó, và lại đọc giá trị mỗi lần
Tôi không chắc liệu CLR 64 bit không được phép lưu trữ giá trị trường trong thanh ghi như đăng ký 32 bit, nhưng nếu có, nó không làm phiền. Có lẽ nó sẽ trong tương lai?
Ở mức độ nào, điều này minh họa cách hoạt động của nền tảng phụ thuộc và có thể thay đổi. Tôi hy vọng điều này sẽ giúp :-)
Tiêu đề của câu hỏi của bạn rộng hơn cần phải đề cập đến tài liệu đang được đề cập đến. Không phải tất cả các mã đều đơn giản như thế này. –
Bạn có so sánh IL của cả hai chương trình không? – Oded
tôi đã so sánh IL nhưng không thực sự nhìn thấy bất cứ điều gì mà sẽ dẫn tôi vào một lời giải thích – dmg