Apakah implementasi berikut, menggunakan inisialisasi lazy, dari Singleton
utas (Meyers 'Singleton) aman?
static Singleton& instance()
{
static Singleton s;
return s;
}
Jika tidak, mengapa dan bagaimana membuatnya aman?
Apakah implementasi berikut, menggunakan inisialisasi lazy, dari Singleton
utas (Meyers 'Singleton) aman?
static Singleton& instance()
{
static Singleton s;
return s;
}
Jika tidak, mengapa dan bagaimana membuatnya aman?
Jawaban:
Di C ++ 11 , ini aman untuk thread. Menurut standar ,
§6.7 [stmt.dcl] p4
:Dukungan GCC dan VS untuk fitur tersebut ( Inisialisasi dan Penghancuran Dinamis dengan Konkurensi , juga dikenal sebagai Magic Statics on MSDN ) adalah sebagai berikut:
Terima kasih kepada @Mankarse dan @olen_gam atas komentar mereka.
Di C ++ 03 , kode ini tidak aman untuk thread. Ada sebuah artikel oleh Meyers yang disebut "C ++ dan Perils of Double-Checked Locking" yang membahas implementasi aman dari pola, dan kesimpulannya adalah, lebih atau kurang, bahwa (dalam C ++ 03) mengunci penuh di sekitar metode instantiating pada dasarnya adalah cara paling sederhana untuk memastikan konkurensi yang tepat pada semua platform, sementara sebagian besar bentuk varian pola penguncian ganda mungkin menderita dari kondisi balapan pada arsitektur tertentu , kecuali instruksi yang disatukan dengan hambatan memori tempat yang strategis.
sumber
Untuk menjawab pertanyaan Anda tentang mengapa ini bukan threadsafe, itu bukan karena panggilan pertama
instance()
harus memanggil konstruktorSingleton s
. Agar aman threads ini harus terjadi di bagian kritis, dan tapi tidak ada persyaratan dalam standar bahwa bagian kritis diambil (standar sampai saat ini benar-benar diam di utas). Compiler sering menerapkan ini menggunakan pemeriksaan sederhana dan penambahan boolean statis - tetapi tidak di bagian kritis. Sesuatu seperti kodesemu berikut:Jadi, inilah Singleton aman-utas sederhana (untuk Windows). Ia menggunakan pembungkus kelas sederhana untuk objek Windows CRITICAL_SECTION sehingga kita dapat membuat kompiler secara otomatis menginisialisasi
CRITICAL_SECTION
sebelummain()
dipanggil. Idealnya kelas bagian kritis RAII yang benar akan digunakan yang dapat menangani pengecualian yang mungkin terjadi ketika bagian kritis diadakan, tapi itu di luar cakupan jawaban ini.Operasi mendasar adalah bahwa ketika sebuah instance
Singleton
diminta, kunci diambil, Singleton dibuat jika perlu, maka kunci dilepaskan dan referensi Singleton dikembalikan.Man - itu banyak omong kosong untuk "membuat global yang lebih baik".
Kelemahan utama implementasi ini (jika saya tidak membiarkan beberapa bug masuk) adalah:
new Singleton()
melempar, kunci tidak akan dilepaskan. Ini dapat diperbaiki dengan menggunakan objek kunci RAII yang benar dan bukan yang sederhana yang saya miliki di sini. Ini juga dapat membantu membuat hal-hal portabel jika Anda menggunakan sesuatu seperti Boost untuk menyediakan pembungkus independen platform untuk kunci.main()
dipanggil - jika Anda menyebutnya sebelum itu (seperti dalam inisialisasi objek statis) hal-hal mungkin tidak berfungsi karenaCRITICAL_SECTION
mungkin tidak diinisialisasi.sumber
new Singleton()
melempar?new Singleton()
melempar pasti ada masalah dengan kunci. Kelas kunci RAII yang tepat harus digunakan, sepertilock_guard
dari Boost. Saya ingin contohnya kurang lebih mandiri, dan itu sudah sedikit monster jadi saya tinggalkan pengecualian keamanan (tapi memanggilnya keluar). Mungkin saya harus memperbaikinya sehingga kode ini tidak mendapatkan cut-n-paste di suatu tempat yang tidak pantas.Melihat standar berikutnya (bagian 6.7.4), ini menjelaskan bagaimana inisialisasi lokal statis aman dari benang. Jadi begitu bagian standar itu diterapkan secara luas, Singleton Meyer akan menjadi implementasi yang disukai.
Saya sudah tidak setuju dengan banyak jawaban. Sebagian besar kompiler sudah menerapkan inisialisasi statis dengan cara ini. Satu-satunya pengecualian adalah Microsoft Visual Studio.
sumber
Jawaban yang benar tergantung pada kompiler Anda. Itu dapat memutuskan untuk membuatnya threadsafe; itu bukan threads "secara alami" aman.
sumber
Pada kebanyakan platform, ini bukan thread-safe. (Tambahkan penafian biasa yang menjelaskan bahwa standar C ++ tidak tahu tentang utas, jadi, secara hukum, ia tidak mengatakan apakah itu benar atau tidak.)
Alasannya bukan karena tidak ada yang mencegah lebih dari satu utas menjalankan
s
konstruktor secara bersamaan ."C ++ dan Perils of Double-Checked Locking" oleh Scott Meyers dan Andrei Alexandrescu adalah risalah yang cukup bagus tentang masalah lajang yang aman.
sumber
Seperti yang dikatakan MSalters: Itu tergantung pada implementasi C ++ yang Anda gunakan. Periksa dokumentasinya. Adapun pertanyaan lainnya: "Jika tidak, mengapa?" - Standar C ++ belum menyebutkan apapun tentang utas. Tetapi versi C ++ yang akan datang sadar akan utas dan secara eksplisit menyatakan bahwa inisialisasi lokal statis adalah utas yang aman. Jika dua utas memanggil fungsi seperti itu, satu utas akan melakukan inisialisasi sementara yang lain akan memblokir & menunggu sampai utas selesai.
sumber