Hanya saja cara variabel kondisi diterapkan (atau awalnya).
Mutex digunakan untuk melindungi variabel kondisi itu sendiri . Itu sebabnya Anda membutuhkannya terkunci sebelum Anda menunggu.
Penantian akan "secara atomis" membuka mutex, memungkinkan orang lain mengakses variabel kondisi (untuk pensinyalan). Kemudian ketika variabel kondisi diisyaratkan atau disiarkan, satu atau lebih utas pada daftar tunggu akan dibangunkan dan mutex akan dikunci secara ajaib lagi untuk utas itu.
Anda biasanya melihat operasi berikut dengan variabel kondisi, menggambarkan cara kerjanya. Contoh berikut adalah utas pekerja yang diberikan pekerjaan melalui sinyal ke variabel kondisi.
thread:
initialise.
lock mutex.
while thread not told to stop working:
wait on condvar using mutex.
if work is available to be done:
do the work.
unlock mutex.
clean up.
exit thread.
Pekerjaan dilakukan dalam loop ini asalkan ada beberapa yang tersedia ketika menunggu kembali. Ketika utas telah ditandai untuk berhenti melakukan pekerjaan (biasanya oleh utas lain yang mengatur kondisi keluar kemudian menendang variabel kondisi untuk membangunkan utas ini), loop akan keluar, mutex akan terbuka dan utas ini akan keluar.
Kode di atas adalah model konsumen tunggal karena mutex tetap terkunci saat pekerjaan sedang dilakukan. Untuk variasi multi-konsumen, Anda dapat menggunakan, sebagai contoh :
thread:
initialise.
lock mutex.
while thread not told to stop working:
wait on condvar using mutex.
if work is available to be done:
copy work to thread local storage.
unlock mutex.
do the work.
lock mutex.
unlock mutex.
clean up.
exit thread.
yang memungkinkan konsumen lain untuk menerima pekerjaan saat ini sedang melakukan pekerjaan.
Variabel kondisi membebaskan Anda dari beban polling suatu kondisi alih-alih membiarkan utas lainnya memberi tahu Anda ketika sesuatu perlu terjadi. Utas lain dapat memberi tahu bahwa utas yang berfungsi tersedia sebagai berikut:
lock mutex.
flag work as available.
signal condition variable.
unlock mutex.
Sebagian besar dari apa yang sering keliru disebut wake ups palsu umumnya selalu karena banyak utas telah ditandai dalam pthread_cond_wait
panggilan mereka (siaran), seseorang akan kembali dengan mutex, melakukan pekerjaan, kemudian menunggu kembali.
Kemudian utas sinyal kedua bisa muncul ketika tidak ada pekerjaan yang harus dilakukan. Jadi Anda harus memiliki variabel tambahan yang menunjukkan bahwa pekerjaan harus dilakukan (ini secara inheren dilindungi mutex dengan pasangan condvar / mutex di sini - utas lain yang diperlukan untuk mengunci mutex sebelum mengubahnya namun).
Ini adalah teknis mungkin untuk thread untuk kembali dari kondisi menunggu tanpa ditendang oleh proses lain (ini adalah wakeup palsu asli) tetapi, dalam semua bertahun-tahun saya bekerja di pthreads, baik dalam pengembangan / jasa dari kode dan sebagai pengguna dari mereka, saya tidak pernah sekalipun menerima salah satu dari ini. Mungkin itu hanya karena HP memiliki implementasi yang layak :-)
Dalam kasus apa pun, kode yang sama yang menangani kasus yang salah juga menangani bangun palsu asli juga karena bendera yang tersedia untuk pekerjaan tidak akan ditetapkan untuk mereka.
do something
di dalamwhile
loop?Variabel kondisi sangat terbatas jika Anda hanya bisa memberi sinyal kondisi, biasanya Anda perlu menangani beberapa data yang terkait dengan kondisi yang ditandai. Pemberian sinyal / bangun harus dilakukan secara atomis untuk mencapai hal itu tanpa memperkenalkan kondisi balapan, atau menjadi terlalu rumit
pthreads juga dapat memberi Anda, karena alasan teknis, bangun palsu . Itu berarti Anda perlu memeriksa predikat, sehingga Anda bisa memastikan kondisinya benar-benar diberi tanda - dan bedakan itu dari bangun palsu. Memeriksa kondisi seperti itu sehubungan dengan menunggu itu perlu dijaga - jadi variabel kondisi membutuhkan cara untuk menunggu / bangun secara atomik sementara mengunci / membuka kunci mutex yang menjaga kondisi itu.
Pertimbangkan contoh sederhana di mana Anda diberitahu bahwa beberapa data dihasilkan. Mungkin utas lain membuat beberapa data yang Anda inginkan, dan menetapkan pointer ke data itu.
Bayangkan utas produsen memberikan beberapa data ke utas konsumen lain melalui penunjuk 'some_data'.
Anda secara alami akan mendapatkan banyak kondisi balapan, bagaimana jika utas lainnya dilakukan
some_data = new_data
tepat setelah Anda bangun, tetapi sebelum Anda melakukannyadata = some_data
Anda juga tidak dapat membuat mutex sendiri untuk menjaga kasus ini .eg
Tidak akan bekerja, masih ada kemungkinan kondisi balapan di antara bangun dan meraih mutex. Menempatkan mutex sebelum pthread_cond_wait tidak membantu Anda, karena Anda sekarang akan memegang mutex sambil menunggu - yaitu produser tidak akan pernah bisa mengambil mutex. (perhatikan, dalam hal ini Anda dapat membuat variabel kondisi kedua untuk memberi sinyal pada produsen yang telah Anda selesaikan
some_data
- meskipun ini akan menjadi rumit, terutama jika Anda menginginkan banyak produsen / konsumen.)Dengan demikian Anda memerlukan cara untuk melepaskan / mengambil mutex secara atom ketika menunggu / bangun dari kondisi tersebut. Itulah yang dilakukan variabel kondisi pthread, dan inilah yang akan Anda lakukan:
(produsen tentu perlu mengambil tindakan pencegahan yang sama, selalu menjaga 'some_data' dengan mutex yang sama, dan memastikan itu tidak menimpa some_data jika some_data saat ini! = NULL)
sumber
while (some_data != NULL)
loop do-while sehingga menunggu variabel kondisi setidaknya sekali?while(some_data != NULL)
menjadiwhile(some_data == NULL)
?Variabel kondisi POSIX adalah stateless. Jadi itu adalah tanggung jawab Anda untuk mempertahankan negara. Karena status akan diakses oleh utas yang menunggu dan utas yang meminta utas lainnya berhenti menunggu, itu harus dilindungi oleh mutex. Jika Anda pikir Anda dapat menggunakan variabel kondisi tanpa mutex, maka Anda belum memahami bahwa variabel kondisi adalah stateless.
Variabel kondisi dibangun di sekitar suatu kondisi. Utas yang menunggu pada variabel kondisi sedang menunggu beberapa kondisi. Utas yang memberi sinyal kondisi variabel mengubah kondisi itu. Misalnya, utas mungkin sedang menunggu beberapa data tiba. Beberapa utas lainnya mungkin memperhatikan bahwa data telah tiba. "Data telah tiba" adalah kondisinya.
Inilah penggunaan klasik dari variabel kondisi, disederhanakan:
Lihat bagaimana utas menunggu pekerjaan. Pekerjaan dilindungi oleh mutex. Menunggu rilis mutex sehingga utas lain dapat memberikan utas ini beberapa pekerjaan. Berikut ini cara memberi isyarat:
Perhatikan bahwa Anda memerlukan mutex untuk melindungi antrian kerja. Perhatikan bahwa variabel kondisi itu sendiri tidak tahu apakah ada pekerjaan atau tidak. Artinya, variabel kondisi harus dikaitkan dengan suatu kondisi, kondisi itu harus dipertahankan oleh kode Anda, dan karena itu dibagi di antara utas, itu harus dilindungi oleh mutex.
sumber
Tidak semua fungsi variabel kondisi memerlukan mutex: hanya operasi menunggu yang melakukannya. Operasi sinyal dan siaran tidak memerlukan mutex. Variabel kondisi juga tidak secara permanen dikaitkan dengan mutex tertentu; mutex eksternal tidak melindungi variabel kondisi. Jika variabel kondisi memiliki keadaan internal, seperti antrian utas tunggu, ini harus dilindungi oleh kunci internal di dalam variabel kondisi.
Operasi menunggu menyatukan variabel kondisi dan mutex, karena:
Untuk alasan ini, operasi tunggu mengambil sebagai argumen baik mutex dan kondisi: sehingga dapat mengelola transfer atom dari thread dari memiliki mutex untuk menunggu, sehingga thread tidak menjadi korban dari kondisi lomba bangun yang hilang .
Kondisi perlombaan wakeup yang hilang akan terjadi jika sebuah thread memberikan mutex, dan kemudian menunggu objek sinkronisasi stateless, tetapi dengan cara yang tidak atom: ada jendela waktu ketika thread tidak lagi memiliki kunci, dan memiliki belum mulai menunggu objek. Selama jendela ini, utas lain dapat masuk, membuat kondisi yang ditunggu menjadi benar, memberi sinyal sinkronisasi tanpa kewarganegaraan dan kemudian menghilang. Objek stateless tidak ingat bahwa itu ditandai (stateless). Jadi, untaian asli tertidur pada objek sinkronisasi stateless, dan tidak bangun, meskipun kondisi yang diperlukan sudah menjadi kenyataan: kehilangan bangun.
Fungsi menunggu variabel kondisi menghindari bangun yang hilang dengan memastikan bahwa utas panggilan terdaftar untuk menangkap wakeup andal sebelum menyerah mutex. Ini tidak mungkin jika fungsi menunggu variabel kondisi tidak mengambil mutex sebagai argumen.
sumber
pthread_cond_broadcast
danpthread_cond_signal
operasi (yang menjadi pertanyaan SO ini) bahkan tidak mengambil mutex sebagai argumen; hanya kondisinya. Spesifikasi POSIX ada di sini . Mutex hanya disebutkan sehubungan dengan apa yang terjadi di utas menunggu ketika mereka bangun.Saya tidak menemukan jawaban lain yang ringkas dan mudah dibaca seperti halaman ini . Biasanya kode tunggu terlihat seperti ini:
Ada tiga alasan untuk membungkus
wait()
dalam mutex:signal()
sebelumwait()
dan kami akan melewatkan ini bangun.check()
tergantung pada modifikasi dari utas lain, jadi Anda tetap harus saling mengucilkan.Poin ketiga tidak selalu menjadi perhatian - konteks historis ditautkan dari artikel ke percakapan ini .
Bangun palsu sering disebutkan sehubungan dengan mekanisme ini (yaitu utas menunggu dibangunkan tanpa
signal()
dipanggil). Namun, kejadian tersebut ditangani oleh yang diulangcheck()
.sumber
Variabel kondisi dikaitkan dengan mutex karena itu adalah satu-satunya cara untuk menghindari perlombaan yang dirancang untuk dihindari.
Pada titik ini, tidak ada utas yang akan memberi sinyal variabel kondisi, jadi utas1 akan menunggu selamanya, meskipun yang dilindungiReadyToRunVariable mengatakan siap untuk digunakan!
Satu-satunya cara untuk mengatasi hal ini adalah agar variabel kondisi melepaskan atom secara bersamaan sambil mulai menunggu variabel kondisi. Inilah sebabnya mengapa fungsi cond_wait membutuhkan mutex
sumber
Mutex seharusnya dikunci saat Anda menelepon
pthread_cond_wait
; ketika Anda menyebutnya secara atomic keduanya membuka mutex dan kemudian memblokir kondisi tersebut. Setelah kondisi diberi sinyal, secara atomis akan menguncinya lagi dan kembali.Ini memungkinkan implementasi penjadwalan yang dapat diprediksi jika diinginkan, di mana utas yang akan melakukan pensinyalan dapat menunggu sampai mutex dilepaskan untuk melakukan pemrosesan dan kemudian memberi sinyal kondisi.
sumber
Saya membuat latihan di kelas jika Anda ingin contoh nyata dari variabel kondisi:
sumber
Tampaknya menjadi keputusan desain khusus daripada kebutuhan konseptual.
Menurut pthreads alasan mengapa mutex tidak dipisahkan adalah bahwa ada peningkatan kinerja yang signifikan dengan menggabungkan mereka dan mereka berharap bahwa karena kondisi balapan umum jika Anda tidak menggunakan mutex, itu hampir selalu akan dilakukan pula.
https://linux.die.net/man/3/pthread_cond_wait
sumber
Ada banyak penafsiran tentang itu, namun saya ingin melambangkannya dengan contoh berikut.
Apa yang salah dengan cuplikan kode? Cukup renungkan sebelum melanjutkan.
Masalahnya benar-benar halus. Jika orang tua memanggil
thr_parent()
dan kemudian memeriksa nilainyadone
, ia akan melihat bahwa itu adalah0
dan dengan demikian mencoba untuk tidur. Tetapi sebelum panggilan menunggu untuk tidur, orang tua terganggu antara baris 6-7, dan anak berlari. Anak mengubah variabel statusdone
ke1
dan sinyal, tetapi tidak ada utas menunggu dan dengan demikian tidak ada utas terbangun. Ketika orangtua berlari lagi, ia tidur selamanya, yang benar-benar mengerikan.Bagaimana jika mereka dilakukan saat memperoleh kunci secara individual?
sumber