Saya kira pengoptimal tertipu oleh kurangnya kata kunci 'volatile' pada isCompletevariabel.
Tentu saja, Anda tidak dapat menambahkannya, karena ini adalah variabel lokal. Dan tentu saja, karena ini adalah variabel lokal, ini tidak diperlukan sama sekali, karena penduduk lokal disimpan di tumpukan dan secara alami selalu "segar".
Namun , setelah dikompilasi, itu bukan lagi variabel lokal . Karena diakses dalam delegasi anonim, kode tersebut dipisahkan, dan diterjemahkan ke dalam kelas pembantu dan bidang anggota, seperti:
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;
}
}
Saya dapat membayangkan sekarang bahwa kompilator / pengoptimal JIT di lingkungan multithread, saat memproses TheHelperkelas, sebenarnya dapat menyimpan nilai falsedalam cache di beberapa bingkai register atau tumpukan pada awalBody() metode, dan tidak pernah menyegarkannya hingga metode berakhir. Itu karena TIDAK ADA JAMINAN bahwa utas & metode TIDAK akan berakhir sebelum "= true" dijalankan, jadi jika tidak ada jaminan, mengapa tidak menyimpannya dan mendapatkan peningkatan kinerja dari membaca objek heap sekali daripada membacanya di setiap pengulangan.
Inilah mengapa kata kunci itu volatileada.
Untuk kelas penolong ini menjadi benar sedikit lebih baik 1) di lingkungan multi-threaded, ia harus memiliki:
public volatile bool isComplete = false;
tetapi, tentu saja, karena ini adalah kode yang dibuat secara otomatis, Anda tidak dapat menambahkannya. Pendekatan yang lebih baik adalah dengan menambahkan beberapa hal lock()di sekitar baca dan tulis isCompleted, atau menggunakan beberapa sinkronisasi siap pakai atau utilitas threading / tasking daripada mencoba melakukannya bare-metal (yang tidak akan terjadi. bare-metal, karena C # di CLR dengan GC, JIT dan (..)).
Perbedaan dalam mode debug terjadi mungkin karena dalam mode debug banyak pengoptimalan yang dikecualikan, jadi Anda dapat men-debug kode yang Anda lihat di layar. Oleh karena while (!isComplete)itu, tidak dioptimalkan sehingga Anda dapat menyetel breakpoint di sana, dan oleh karena isCompleteitu tidak disimpan dalam cache secara agresif dalam register atau tumpukan saat metode dimulai dan dibaca dari objek di heap pada setiap iterasi pengulangan.
BTW. Itu hanya tebakan saya tentang itu. Saya bahkan tidak mencoba untuk mengkompilasinya.
BTW. Sepertinya bukan bug; itu lebih seperti efek samping yang sangat tidak jelas. Juga, jika saya benar tentang itu, maka itu mungkin kekurangan bahasa - C # harus memungkinkan untuk menempatkan kata kunci 'volatile' pada variabel lokal yang ditangkap dan dipromosikan ke bidang anggota di closures.
1) lihat di bawah untuk komentar dari Eric Lippert sekitar volatiledan / atau artikel ini sangat menarik yang menunjukkan tingkat kompleksitas yang terlibat dalam memastikan bahwa kode mengandalkan volatileadalah aman ..uh, baik ..uh, katakanlah OK.