Benarkah C ++ 0x akan datang tanpa semaphores? Sudah ada beberapa pertanyaan tentang Stack Overflow mengenai penggunaan semaphores. Saya menggunakannya (posix semaphores) setiap saat untuk membiarkan utas menunggu acara di utas lain:
void thread0(...)
{
doSomething0();
event1.wait();
...
}
void thread1(...)
{
doSomething1();
event1.post();
...
}
Jika saya akan melakukannya dengan mutex:
void thread0(...)
{
doSomething0();
event1.lock(); event1.unlock();
...
}
void thread1(...)
{
event1.lock();
doSomethingth1();
event1.unlock();
...
}
Masalah: Ini jelek dan tidak dijamin bahwa thread1 mengunci mutex pertama (Mengingat bahwa thread yang sama harus mengunci dan membuka kunci mutex, Anda juga tidak dapat mengunci event1 sebelum thread0 dan thread1 dimulai).
Jadi karena boost juga tidak memiliki semaphores, apa cara paling sederhana untuk mencapai hal di atas?
Jawaban:
Anda dapat dengan mudah membangunnya dari mutex dan variabel kondisi:
sumber
while(!count_)
loop.Berdasarkan jawaban Maxim Yegorushkin , saya mencoba membuat contoh dalam gaya C ++ 11.
sumber
cv.wait(lck, [this]() { return count > 0; });
Saya memutuskan untuk menulis semaphore C ++ 11 yang paling kuat / generik yang saya bisa, dengan gaya standar sebanyak yang saya bisa (perhatikan
using semaphore = ...
, Anda biasanya hanya akan menggunakan nama yangsemaphore
mirip dengan yang biasanyastring
tidak menggunakanbasic_string
):sumber
wait_for
danwait_until
metode panggilan dengan predikat mengembalikan nilai boolean (bukan `std :: cv_status).std::size_t
tidak ditandatangani sehingga mengurangi di bawah nol adalah UB, dan itu akan selalu terjadi>= 0
. IMHOcount
harus menjadiint
.sesuai dengan posix semaphores, saya akan menambahkan
Dan saya lebih suka menggunakan mekanisme sinkronisasi pada tingkat abstraksi yang nyaman, daripada selalu menyalin paste versi yang dijahit menggunakan operator yang lebih dasar.
sumber
Anda juga dapat melihat cpp11-on-multicore - ini memiliki implementasi semaphore yang portabel dan optimal.
Repositori juga berisi barang threading lain yang melengkapi threading c ++ 11.
sumber
Anda dapat bekerja dengan variabel mutex dan kondisi. Anda mendapatkan akses eksklusif dengan mutex, periksa apakah Anda ingin melanjutkan atau perlu menunggu ujung lainnya. Jika Anda perlu menunggu, Anda menunggu dalam kondisi. Ketika utas lainnya menentukan bahwa Anda dapat melanjutkan, itu menandakan kondisi.
Ada contoh singkat di pustaka boost :: thread yang kemungkinan besar bisa Anda salin (C ++ 0x dan boost lib thread sangat mirip).
sumber
wait()
diterjemahkan ke "kunci, periksa jumlah jika pengurangan tidak nol dan lanjutkan; jika nol menunggu dengan kondisi" sementarapost
akan "mengunci, penghitung kenaikan, memberi sinyal jika itu 0 "Juga dapat bermanfaat pembungkus RAII semaphore di utas:
Contoh penggunaan dalam aplikasi multithread:
sumber
C ++ 20 akhirnya akan memiliki semaphores -
std::counting_semaphore<max_count>
.Ini akan memiliki (setidaknya) metode berikut:
acquire()
(pemblokiran)try_acquire()
(tanpa pemblokiran, mengembalikan dengan segera)try_acquire_for()
(non-blocking, membutuhkan waktu)try_acquire_until()
(non-blocking, membutuhkan waktu untuk berhenti mencoba)release()
Ini belum terdaftar di cppreference, tetapi Anda dapat membaca slide presentasi CppCon 2019 ini , atau menonton video . Ada juga proposal resmi P0514R4 , tapi saya tidak yakin itu versi yang paling mutakhir.
sumber
Saya menemukan shared_ptr dan lemah_ptr, panjang dengan daftar, melakukan pekerjaan yang saya butuhkan. Masalah saya adalah, saya punya beberapa klien yang ingin berinteraksi dengan data internal host. Biasanya, tuan rumah memperbarui data itu sendiri, namun, jika klien memintanya, tuan rumah harus berhenti memperbarui hingga tidak ada klien yang mengakses data host. Pada saat yang sama, klien dapat meminta akses eksklusif, sehingga tidak ada klien lain, atau tuan rumah, yang dapat mengubah data host tersebut.
Bagaimana saya melakukan ini, saya membuat sebuah struct:
Setiap klien akan memiliki anggota seperti:
Kemudian tuan rumah akan memiliki anggota lemah_ptr untuk eksklusivitas, dan daftar lemah_ptr untuk kunci non-eksklusif:
Ada fungsi untuk mengaktifkan kunci, dan fungsi lainnya untuk memeriksa apakah host terkunci:
Saya menguji kunci di LockUpdate, IsUpdateLocked, dan secara berkala di rutin pembaruan host. Menguji kunci adalah sesederhana memeriksa apakah lemah_ptr kadaluarsa, dan menghapus apa pun yang kadaluwarsa dari daftar m_locks (saya hanya melakukan ini selama pembaruan host), saya dapat memeriksa apakah daftar kosong; pada saat yang sama, saya mendapatkan pembukaan kunci otomatis ketika klien me-reset shared_ptr yang mereka gunakan, yang juga terjadi ketika klien dihancurkan secara otomatis.
Efek keseluruhannya adalah, karena klien jarang memerlukan eksklusivitas (biasanya dicadangkan untuk tambahan dan penghapusan saja), sebagian besar waktu permintaan untuk LockUpdate (false), yaitu non-eksklusif, berhasil selama (! M_exclusiveLock). Dan LockUpdate (true), permintaan eksklusivitas, hanya berhasil ketika keduanya (! M_exclusiveLock) dan (m_locks.empty ()).
Antrian dapat ditambahkan untuk mengurangi antara kunci eksklusif dan non-eksklusif, namun, saya belum memiliki tabrakan sejauh ini, jadi saya bermaksud menunggu sampai itu terjadi untuk menambahkan solusi (kebanyakan jadi saya memiliki kondisi pengujian dunia nyata).
Sejauh ini ini bekerja dengan baik untuk kebutuhan saya; Saya bisa membayangkan perlunya memperluas ini, dan beberapa masalah yang mungkin timbul karena penggunaan yang diperluas, namun, ini cepat untuk diterapkan, dan hanya memerlukan sedikit kode khusus.
sumber
Jika seseorang tertarik pada versi atom, ini adalah implementasinya. Kinerja diharapkan lebih baik daripada versi variabel mutex & kondisi.
sumber
wait
kode harus berulang beberapa kali. Ketika akhirnya membuka blokir, itu akan mengambil ibu dari semua cabang yang salah diprediksi sebagai prediksi loop CPU pasti akan memprediksi itu akan berulang lagi. Saya bisa mendaftar lebih banyak masalah dengan kode ini.wait
Loop akan mengkonsumsi sumber daya eksekusi mikro CPU saat berputar. Misalkan ada di inti fisik yang sama dengan utas yang seharusnyanotify
- itu akan memperlambat utas itu.wait
lingkaran untuk semaphore yang sama. Keduanya menulis dengan kecepatan penuh ke baris cache yang sama , yang dapat memperlambat core lainnya untuk merayap dengan menjenuhkan bus antar-inti.