Bilakah menggunakan kata kunci tidak stabil dalam C #

Teknik pengoptimuman yang digunakan oleh penyusun JIT (just-in-time) dalam Common Language Runtime mungkin membawa kepada hasil yang tidak dapat diramalkan apabila program .Net anda berusaha melakukan pembacaan data yang tidak mudah berubah dalam senario multithreaded. Dalam artikel ini kita akan melihat perbezaan antara akses memori tidak stabil dan tidak mudah berubah, peranan kata kunci tidak stabil dalam C #, dan bagaimana kata kunci tidak stabil harus digunakan.

Saya akan memberikan beberapa contoh kod dalam C # untuk menggambarkan konsep. Untuk memahami bagaimana kata kunci tidak stabil berfungsi, pertama kita perlu memahami bagaimana strategi pengoptimuman penyusun JIT berfungsi di .Net.

Memahami pengoptimuman penyusun JIT

Perlu diingatkan bahawa penyusun JIT akan, sebagai sebahagian daripada strategi pengoptimuman, mengubah susunan bacaan dan penulisan dengan cara yang tidak mengubah makna dan hasil akhirnya program. Ini digambarkan dalam coretan kod yang diberikan di bawah.

x = 0;

x = 1;

Coretan kod di atas dapat diubah menjadi berikut — sambil mempertahankan semantik asli program.

x = 1;

Penyusun JIT juga dapat menerapkan konsep yang disebut "penyebaran berterusan" untuk mengoptimumkan kod berikut.

x = 1;

y = x;

Coretan kod di atas boleh diubah menjadi yang berikut — sekali lagi sambil mengekalkan semantik asal program.

x = 1;

y = 1;

Akses memori yang tidak menentu dan tidak mudah berubah

Model memori sistem moden cukup rumit. Anda mempunyai daftar pemproses, pelbagai peringkat cache, dan memori utama yang dikongsi oleh beberapa pemproses. Apabila program anda dijalankan, pemproses mungkin menyimpan data dan kemudian mengakses data ini dari cache apabila diminta oleh utas pelaksana. Kemas kini dan pembacaan data ini mungkin bertentangan dengan versi data yang di-cache, sementara memori utama akan dikemas kini di kemudian hari. Model penggunaan memori ini mempunyai akibat untuk aplikasi multithreaded. 

Apabila satu utas berinteraksi dengan data dalam cache, dan utas kedua cuba membaca data yang sama secara serentak, utas kedua mungkin membaca versi data yang ketinggalan zaman dari memori utama. Ini kerana apabila nilai objek yang tidak mudah berubah diperbaharui, perubahan dibuat dalam cache utas pelaksana dan bukan dalam memori utama. Namun, ketika nilai objek yang mudah berubah diperbarui, bukan hanya perubahan yang dibuat dalam cache utas pelaksana, tetapi cache ini kemudian dialirkan ke memori utama. Dan apabila nilai objek yang tidak stabil dibaca, utas itu menyegarkan cache dan dan membaca nilai yang dikemas kini.

Menggunakan kata kunci tidak stabil dalam C #

Kata kunci tidak menentu di C # digunakan untuk memberitahu penyusun JIT bahawa nilai pemboleh ubah tidak boleh dicache kerana mungkin diubah oleh sistem operasi, perkakasan, atau thread yang dijalankan secara serentak. Oleh itu, penyusun mengelak daripada menggunakan sebarang pengoptimuman pada pemboleh ubah yang mungkin menyebabkan konflik data, iaitu untuk benang yang berbeza yang mengakses nilai pemboleh ubah yang berbeza.

Apabila anda menandakan objek atau pemboleh ubah sebagai tidak stabil, ia menjadi calon untuk membaca dan menulis yang tidak stabil. Perlu diingatkan bahawa dalam C # semua penulisan memori tidak stabil tanpa mengira sama ada anda menulis data ke objek yang tidak menentu atau tidak mudah berubah. Walau bagaimanapun, kekaburan berlaku semasa anda membaca data. Apabila anda membaca data yang tidak mudah berubah, utas pelaksanaan mungkin atau tidak selalu mendapat nilai terkini. Sekiranya objek itu tidak stabil, utasnya selalu mendapat nilai terkini.

Anda boleh menyatakan pemboleh ubah sebagai tidak stabil dengan mendahului dengan volatilekata kunci. Coretan kod berikut menggambarkan ini.

Program kelas

    {

        int tidak menentu awam;

        kekosongan statik Utama (string [] args)

        {

            // Tuliskan kod anda di sini

        }

    }

Anda boleh menggunakan volatilekata kunci dengan rujukan, penunjuk, dan jenis enum. Anda juga boleh menggunakan pengubah mudah ubah dengan jenis bait, pendek, int, char, float, dan bool. Harus diingat bahawa pemboleh ubah tempatan tidak dapat dinyatakan sebagai tidak stabil. Apabila anda menentukan objek jenis rujukan sebagai mudah berubah, hanya penunjuk (bilangan bulat 32-bit yang menunjuk ke lokasi dalam memori di mana objek itu sebenarnya disimpan) tidak stabil, bukan nilai contohnya. Juga, pemboleh ubah berganda tidak boleh berubah kerana ukurannya 64 bit, lebih besar daripada ukuran perkataan pada sistem x86. Sekiranya anda perlu membuat pemboleh ubah berganda tidak stabil, anda harus membungkusnya di dalam kelas. Anda boleh melakukannya dengan mudah dengan membuat kelas pembungkus seperti yang ditunjukkan dalam coretan kod di bawah.

kelas awam VolatileDoubleDemo

{

    peribadi volatile WrappedVolatileDouble volatileData;

}

kelas awam WrappedVolatileDouble

{

    Data berganda awam {get; menetapkan; }

Walau bagaimanapun, perhatikan had contoh kod di atas. Walaupun anda mempunyai nilai volatileDatapenunjuk rujukan terbaru , anda tidak dijamin nilai terkini Dataharta tanah. Penyelesaian ini adalah menjadikan WrappedVolatileDoublejenisnya tidak berubah.

Walaupun kata kunci tidak stabil dapat membantu anda dalam keselamatan utas dalam situasi tertentu, itu bukan penyelesaian untuk semua masalah serentak utas anda. Anda harus tahu bahawa menandai pemboleh ubah atau objek sebagai tidak stabil tidak bermaksud anda tidak perlu menggunakan kata kunci kunci. Kata kunci tidak stabil bukan pengganti kata kunci kunci. Itu hanya untuk membantu anda mengelakkan konflik data apabila anda mempunyai banyak utas yang cuba mengakses data yang sama.