Tôi đoán rằng trình tối ưu hóa bị đánh lừa bởi thiếu từ khóa 'dễ bay hơi' trên isComplete
biến.
Tất nhiên, bạn không thể thêm nó, vì nó là một biến cục bộ. Và tất nhiên, vì nó là một biến cục bộ, nó không cần thiết chút nào, bởi vì các biến địa phương được lưu giữ trên chồng và chúng đương nhiên luôn "tươi".
Tuy nhiên , sau khi biên dịch, nó không còn là một biến cục bộ nữa. Vì nó được truy cập trong một đại biểu ẩn danh, mã được tách và nó được dịch thành một lớp trợ giúp và trường thành viên, giống như:
public static void Main(string[] args)
{
TheHelper hlp = new TheHelper();
var t = new Thread(hlp.Body);
t.Start();
Thread.Sleep(500);
hlp.isComplete = true;
t.Join();
Console.WriteLine("complete!");
}
private class TheHelper
{
public bool isComplete = false;
public void Body()
{
int i = 0;
while (!isComplete) i += 0;
}
}
Bây giờ tôi có thể tưởng tượng rằng trình biên dịch / tối ưu hóa JIT trong môi trường đa luồng, khi xử lý TheHelper
lớp, thực sự có thể lưu giá trị false
vào bộ nhớ cache trong một số thanh ghi hoặc khung ngăn xếp ở đầu Body()
phương thức và không bao giờ làm mới nó cho đến khi phương thức kết thúc. Đó là bởi vì KHÔNG CÓ ĐẢM BẢO rằng luồng & phương thức sẽ KHÔNG kết thúc trước khi "= true" được thực thi, vì vậy nếu không có gì đảm bảo, thì tại sao không lưu vào bộ nhớ cache và tăng hiệu suất đọc đối tượng heap một lần thay vì đọc nó mọi lúc sự lặp lại.
Đây chính là lý do tại sao từ khóa volatile
tồn tại.
Để lớp trợ giúp này chính xác tốt hơn một chút 1) trong môi trường đa luồng, nó phải có:
public volatile bool isComplete = false;
nhưng, tất nhiên, vì nó là mã được tạo tự động, bạn không thể thêm nó. Một cách tiếp cận tốt hơn sẽ là thêm một số lock()
s xung quanh việc đọc và ghi isCompleted
, hoặc sử dụng một số tiện ích đồng bộ hóa / phân luồng / tác vụ sẵn sàng sử dụng khác thay vì cố gắng làm điều đó bằng kim loại trần (mà nó sẽ không phải là kim loại trần, vì nó là C # trên CLR với GC, JIT và (..)).
Sự khác biệt trong chế độ gỡ lỗi xảy ra có thể là do trong chế độ gỡ lỗi, nhiều tính năng tối ưu bị loại trừ, vì vậy bạn có thể gỡ lỗi mã mà bạn thấy trên màn hình. Do đó while (!isComplete)
không được tối ưu hóa để bạn có thể đặt một điểm ngắt ở đó và do đó isComplete
không được lưu trữ tích cực trong một thanh ghi hoặc ngăn xếp khi bắt đầu phương thức và được đọc từ đối tượng trên heap ở mỗi lần lặp vòng lặp.
BTW. Đó chỉ là suy đoán của tôi về điều đó. Tôi thậm chí đã không cố gắng biên dịch nó.
BTW. Nó dường như không phải là một lỗi; nó giống như một tác dụng phụ rất khó hiểu. Ngoài ra, nếu tôi nói đúng về nó, thì đó có thể là một sự thiếu sót về ngôn ngữ - C # nên cho phép đặt từ khóa 'variable' trên các biến cục bộ được nắm bắt và thăng cấp cho các trường thành viên trong phần đóng.
1) xem bên dưới để biết nhận xét từ Eric Lippert về volatile
và / hoặc bài viết rất thú vị này cho thấy mức độ phức tạp liên quan đến việc đảm bảo rằng mã dựa vào volatile
là an toàn ..uh, tốt ..uh, hãy nói OK.