Ungkapan "sangat terjadi sebelum" digunakan beberapa kali dalam standar konsep C ++.
Misalnya: Pengakhiran [basic.start.term] / 5
Jika penyelesaian inisialisasi objek dengan durasi penyimpanan statis sangat terjadi sebelum panggilan ke std :: atexit (lihat, [support.start.term]), panggilan ke fungsi diteruskan ke std :: atexit diurutkan sebelum panggilan ke destruktor untuk objek. Jika panggilan ke std :: atexit sangat terjadi sebelum penyelesaian inisialisasi objek dengan durasi penyimpanan statis, panggilan ke destruktor untuk objek tersebut diurutkan sebelum panggilan ke fungsi diteruskan ke std :: atexit . Jika panggilan ke std :: atexit sangat terjadi sebelum panggilan lain ke std :: atexit, panggilan ke fungsi diteruskan ke panggilan std :: atexit kedua diurutkan sebelum panggilan ke fungsi dialihkan ke std pertama :: panggilan atexit.
Dan didefinisikan dalam Data races [intro.races] / 12
Evaluasi A sangat terjadi sebelum evaluasi D jika, baik
(12.1) A diurutkan sebelum D, atau
(12.2) A menyinkronkan dengan D, dan baik A maupun D adalah operasi atom yang konsisten secara berurutan ([urutan atom]), atau
(12.3) ada evaluasi B dan C sehingga A diurutkan sebelum B, B hanya terjadi sebelum C, dan C diurutkan sebelum D, atau
(12.4) ada evaluasi B sehingga A sangat terjadi sebelum B, dan B sangat terjadi sebelum D.
[Catatan: Secara informal, jika A sangat terjadi sebelum B, maka A tampaknya dievaluasi sebelum B dalam semua konteks. Sangat terjadi sebelum mengecualikan operasi konsumsi. - catatan akhir]
Mengapa "sangat terjadi sebelum" diperkenalkan? Secara intuitif, apa perbedaan dan hubungannya dengan "terjadi sebelumnya"?
Apa arti "A tampaknya dievaluasi sebelum B dalam semua konteks" dalam catatan?
(Catatan: motivasi untuk pertanyaan ini adalah komentar Peter Cordes di bawah jawaban ini .)
Tambahan kutipan standar draft (terima kasih kepada Peter Cordes)
Ketertiban dan konsistensi [atomics.order] / 4
Ada urutan total tunggal S pada semua operasi memory_order :: seq_cst, termasuk pagar, yang memenuhi batasan berikut. Pertama, jika A dan B adalah operasi memory_order :: seq_cst dan A sangat terjadi sebelum B, maka A mendahului B dalam S. Kedua, untuk setiap pasangan operasi atom A dan B pada objek M, di mana A diperintahkan koherensi sebelum B, empat kondisi berikut ini harus dipenuhi oleh S:
(4.1) jika A dan B keduanya operasi memory_order :: seq_cst, maka A mendahului B dalam S; dan
(4.2) jika A adalah operasi memory_order :: seq_cst dan B terjadi sebelum memory_order :: seq_cst pagar Y, maka A mendahului Y di S; dan
(4.3) jika memory_order :: seq_cst pagar X terjadi sebelum A dan B adalah memory_order :: seq_cst operasi, maka X mendahului B dalam S; dan
(4.4) jika memory_order :: seq_cst pagar X terjadi sebelum A dan B terjadi sebelum memory_order :: seq_cst pagar Y, maka X mendahului Y di S.
sumber
seq_cst
, dalam Atom 31.4 Urutan dan konsistensi: 4 . Itu tidak dalam standar C ++ 17 n4659 , di mana 32,4 - 3 mendefinisikan keberadaan satu urutan total operasi seq_cst yang konsisten dengan pesanan "terjadi sebelumnya" dan pesanan modifikasi untuk semua lokasi yang terpengaruh ; "sangat" ditambahkan dalam konsep kemudian.atexit()
satu utas danexit()
lainnya, itu tidak cukup bagi inisialisasi untuk hanya membawa ketergantungan berbasis konsumsi hanya karena hasilnya kemudian berbeda dari jikaexit()
dipanggil oleh utas yang sama. Jawaban saya yang lebih tua menyangkut perbedaan ini.exit()
. Setiap utas dapat mematikan seluruh program dengan keluar, atau utas utama dapat keluar denganreturn
-ing. Ini menghasilkan panggilanatexit()
penangan dan kematian semua utas apa pun yang mereka lakukan.Jawaban:
Persiapkan diri Anda untuk "terjadi begitu saja sebelum" juga! Lihatlah snapshot cppref saat ini https://en.cppreference.com/w/cpp/atomic/memory_order
Tampaknya "cukup terjadi sebelum" ditambahkan dalam C ++ 20.
Jadi Simply-HB dan HB adalah sama kecuali untuk bagaimana mereka menangani operasi konsumsi. Lihat HB
Bagaimana mereka berbeda dalam hal mengkonsumsi? Lihat Inter-Thread-HB
Operasi yang dipesan ketergantungan (yaitu menggunakan rilis / konsumsi) adalah HB tetapi tidak harus Cukup-HB.
Mengkonsumsi lebih santai daripada memperoleh, jadi jika saya mengerti dengan benar, HB lebih santai dari pada sekadar-HB.
Jadi operasi rilis / konsumsi tidak boleh Sangat-HB.
Rilis / akuisisi dapat berupa HB dan Cukup-HB (karena rilis / perolehan disinkronkan dengan) tetapi tidak harus Sangat-HB. Karena Strongly-HB secara khusus mengatakan bahwa A harus disinkronkan-dengan B DAN menjadi operasi Konsisten Berurutan.
Semua konteks: Semua utas / semua CPU melihat (atau "pada akhirnya akan menyetujui") urutan yang sama. Ini adalah jaminan konsistensi berurutan - urutan modifikasi total global semua variabel. Rantai perolehan / rilis hanya menjamin urutan modifikasi yang dirasakan untuk utas yang berpartisipasi dalam rantai. Utas di luar rantai secara teori diizinkan untuk melihat urutan yang berbeda.
Saya tidak tahu mengapa Strongly-HB dan Simply-HB diperkenalkan. Mungkin untuk membantu memperjelas cara beroperasi di sekitar konsumsi? Sangat-HB memiliki sifat yang bagus - jika satu thread mengamati A kuat-terjadi-sebelum B, ia tahu semua utas akan mengamati hal yang sama.
Sejarah mengkonsumsi:
Paul E. McKenney bertanggung jawab untuk mengkonsumsi dalam standar C dan C ++. Mengkonsumsi menjamin pemesanan antara penugasan pointer dan memori yang ditunjuknya. Itu diciptakan karena DEC Alpha. DEC Alpha secara spekulatif dapat melakukan dereferensi pointer, sehingga ia juga memiliki pagar memori untuk mencegah hal ini. DEC Alpha tidak lagi dibuat dan saat ini tidak ada prosesor yang memiliki perilaku ini. Konsumsi dimaksudkan untuk menjadi sangat santai.
sumber
mo_consume
dimaksudkan untuk mengambil keuntungan dari urutan ketergantungan data pada CPU nyata, dan memformalkan bahwa kompiler tidak dapat memecah ketergantungan data melalui prediksi cabang. misalnyaint *p = load();
tmp = *p;
bisa rusak oleh kompilator memperkenalkanif(p==known_address) tmp = *known_address; else tmp=*p;
jika itu memiliki beberapa alasan untuk mengharapkan nilai pointer tertentu menjadi umum. Itu legal untuk santai tetapi tidak mengkonsumsinya.