Variabel Bersyarat vs Semaphore

Jawaban:

207

Kunci digunakan untuk saling mengesampingkan. Jika Anda ingin memastikan bahwa sepotong kode adalah atom, letakkan kunci di sekitarnya. Anda secara teoritis dapat menggunakan semafor biner untuk melakukan ini, tetapi itu kasus khusus.

Variabel semaphore dan kondisi dibangun di atas pengecualian timbal balik yang disediakan oleh kunci dan digunakan untuk menyediakan akses yang disinkronkan ke sumber daya bersama. Mereka dapat digunakan untuk tujuan serupa.

Variabel kondisi umumnya digunakan untuk menghindari menunggu sibuk (mengulang berulang kali saat memeriksa kondisi) sambil menunggu sumber daya tersedia. Misalnya, jika Anda memiliki utas (atau beberapa utas) yang tidak dapat melanjutkan seterusnya sampai antrian kosong, pendekatan menunggu sibuk akan hanya melakukan sesuatu seperti:

//pseudocode
while(!queue.empty())
{
   sleep(1);
}

Masalahnya adalah Anda membuang-buang waktu prosesor dengan meminta utas ini berulang kali memeriksa kondisinya. Mengapa tidak memiliki variabel sinkronisasi yang dapat diberi isyarat untuk memberi tahu thread bahwa sumber daya tersedia?

//pseudocode
syncVar.lock.acquire();

while(!queue.empty())
{
   syncVar.wait();
}

//do stuff with queue

syncVar.lock.release();

Agaknya, Anda akan memiliki utas di tempat lain yang menarik hal-hal keluar dari antrean. Saat antrean kosong, antrean dapat memanggil syncVar.signal()untuk mengaktifkan utas acak yang tertidur syncVar.wait()(atau biasanya juga ada metode signalAll()atau broadcast()untuk mengaktifkan semua utas yang menunggu).

Saya biasanya menggunakan variabel sinkronisasi seperti ini ketika saya memiliki satu atau lebih utas yang menunggu pada satu kondisi tertentu (misalnya agar antrian kosong).

Semaphore dapat digunakan dengan cara yang sama, tetapi saya pikir mereka lebih baik digunakan ketika Anda memiliki sumber daya bersama yang dapat tersedia dan tidak tersedia berdasarkan beberapa bilangan bulat dari hal-hal yang tersedia. Semaphore baik untuk situasi produsen / konsumen di mana produsen mengalokasikan sumber daya dan konsumen mengkonsumsinya.

Pikirkan apakah Anda memiliki mesin penjual soda. Hanya ada satu mesin soda dan itu adalah sumber daya bersama. Anda memiliki satu utas yang merupakan vendor (produsen) yang bertanggung jawab untuk menjaga stok mesin dan utas N yang merupakan pembeli (konsumen) yang ingin mengeluarkan soda dari mesin. Jumlah soda di mesin adalah nilai integer yang akan menggerakkan semaphore kita.

Setiap pembeli (konsumen) benang yang datang ke mesin soda memanggil down()metode semaphore untuk mengambil soda. Ini akan mengambil soda dari mesin dan mengurangi jumlah soda yang tersedia menjadi 1. Jika ada soda yang tersedia, kode akan terus berjalan melewati down()pernyataan tanpa masalah. Jika tidak ada soda yang tersedia, utas akan tidur di sini menunggu untuk diberi tahu kapan soda tersedia lagi (ketika ada lebih banyak soda di mesin).

Benang vendor (produsen) pada dasarnya akan menunggu mesin soda kosong. Penjual diberi tahu saat soda terakhir diambil dari mesin (dan satu atau lebih konsumen berpotensi menunggu untuk mengeluarkan soda). Vendor akan mengisi kembali mesin soda dengan up()metode semaphore , jumlah soda yang tersedia akan bertambah setiap kali dan dengan demikian benang konsumen yang menunggu akan diberi tahu bahwa lebih banyak soda tersedia.

Metode wait()dan signal()dari variabel sinkronisasi cenderung disembunyikan di dalam operasi down()dan up()semaphore.

Pastinya ada tumpang tindih antara kedua pilihan tersebut. Ada banyak skenario di mana semafor atau variabel kondisi (atau kumpulan variabel kondisi) dapat memenuhi tujuan Anda. Variabel semaphore dan condition dikaitkan dengan objek kunci yang mereka gunakan untuk mempertahankan pengecualian timbal balik, tetapi kemudian keduanya menyediakan fungsionalitas tambahan di atas kunci untuk menyinkronkan eksekusi thread. Terserah Anda untuk mencari tahu mana yang paling masuk akal untuk situasi Anda.

Itu belum tentu merupakan deskripsi yang paling teknis, tapi itulah yang masuk akal di kepala saya.

Brent Menulis Kode
sumber
9
Jawaban bagus, saya ingin menambahkan dari yang lain jadi jawaban: Semaphore digunakan untuk mengontrol jumlah utas yang dieksekusi. Akan ada serangkaian sumber daya yang tetap. Jumlah resource akan berkurang setiap kali thread memiliki yang sama. Ketika jumlah semaphore mencapai 0 maka tidak ada utas lain yang diizinkan untuk mendapatkan sumber daya. Utas diblokir sampai utas lain yang memiliki rilis sumber daya. Singkatnya, perbedaan utamanya adalah berapa banyak utas yang diizinkan untuk memperoleh sumber daya sekaligus? Mutex --Ya SATU. Semaphore - DEFINED_COUNT nya, (sebanyak semaphore count)
berkay
10
Hanya untuk menguraikan mengapa ada while loop, bukan if: sesuatu yang disebut spurios wakeup . Mengutip artikel wikipedia ini : "Salah satu alasan untuk ini adalah bangun palsu; yaitu, utas mungkin dibangunkan dari status menunggu meskipun tidak ada utas yang memberi sinyal variabel kondisi"
Vladislavs Burakovs
3
@VladislavlavsBurakovs Poin yang bagus! Saya rasa ini juga berguna untuk kasus di mana siaran mengaktifkan lebih banyak utas daripada sumber daya yang tersedia (misalnya siaran membangunkan 3 utas, tetapi hanya ada 2 item dalam antrean).
Brent Menulis Kode
Saya berharap saya akan memberi suara positif untuk jawaban Anda sampai antrian penuh;) Jawaban Sempurna. Kode ini dapat membantu mencari tahu semaphores csc.villanova.edu/~mdamian/threads/PC.htm
Mohamad-Jaafar NEHME
3
@VladislavsBurakovs Untuk mengklarifikasi sedikit, alasan bahwa kondisi tersebut mungkin masih salah untuk utas yang baru saja bangun (mengakibatkan bangun palsu) adalah karena mungkin telah ada sakelar konteks sebelum utas mendapat kesempatan untuk memeriksa kondisinya sekali lagi, di mana beberapa utas terjadwal lainnya membuat kondisi itu salah. Ini adalah salah satu alasan yang saya tahu untuk bangun palsu, tidak tahu apakah masih ada lagi.
Maksimal
52

Mari kita ungkapkan apa yang ada di balik tenda.

Variabel bersyarat pada dasarnya adalah antrian tunggu , yang mendukung operasi pemblokiran-tunggu dan bangun, yaitu Anda dapat meletakkan utas ke antrian tunggu dan menyetel statusnya ke BLOKIR, dan mengeluarkan utas darinya dan menyetel statusnya ke SIAP.

Perhatikan bahwa untuk menggunakan variabel bersyarat, dibutuhkan dua elemen lain:

  • suatu kondisi (biasanya diterapkan dengan memeriksa bendera atau penghitung)
  • mutex yang melindungi kondisi tersebut

Protokol kemudian menjadi,

  1. memperoleh mutex
  2. periksa kondisi
  3. blok dan lepaskan mutex jika kondisinya benar, jika tidak, lepaskan mutex

Semaphore pada dasarnya adalah penghitung + mutex + antrian tunggu. Dan itu dapat digunakan apa adanya tanpa ketergantungan eksternal. Anda dapat menggunakannya sebagai mutex atau sebagai variabel bersyarat.

Oleh karena itu, semaphore dapat diperlakukan sebagai struktur yang lebih canggih daripada variabel bersyarat, sedangkan variabel bersyarat lebih ringan dan fleksibel.

cucufrog
sumber
mutex dapat dilihat sebagai variabel kondisi, apakah kondisinya dipertahankan atau tidak.
宏杰 李
18

Semaphore dapat digunakan untuk mengimplementasikan akses eksklusif ke variabel, namun mereka dimaksudkan untuk digunakan untuk sinkronisasi. Mutex, di sisi lain, memiliki semantik yang sangat terkait dengan pengecualian timbal balik: hanya proses yang mengunci sumber daya yang diizinkan untuk membukanya.

Sayangnya Anda tidak dapat mengimplementasikan sinkronisasi dengan mutex, itulah mengapa kami memiliki variabel kondisi. Juga perhatikan bahwa dengan variabel kondisi Anda dapat membuka kunci semua utas menunggu dalam sekejap dengan menggunakan pembukaan kunci siaran. Ini tidak dapat dilakukan dengan semaphore.

Dacav
sumber
9

variabel semaphore dan condition sangat mirip dan sebagian besar digunakan untuk tujuan yang sama. Namun, ada perbedaan kecil yang membuat seseorang lebih disukai. Misalnya, untuk mengimplementasikan sinkronisasi penghalang, Anda tidak akan dapat menggunakan semaphore. Tetapi variabel kondisi sangat ideal.

Sinkronisasi penghalang adalah saat Anda ingin semua utas menunggu hingga semua orang tiba di bagian tertentu dalam fungsi utas. ini dapat diterapkan dengan memiliki variabel statis yang awalnya nilai total utas dikurangi oleh setiap utas ketika mencapai penghalang itu. ini berarti kita ingin setiap utas tidur sampai yang terakhir tiba. Semaphore akan melakukan sebaliknya! dengan semaphore, setiap utas akan terus berjalan dan utas terakhir (yang akan menyetel nilai semafor ke 0) akan tidur.

variabel kondisi di sisi lain, adalah ideal. ketika setiap utas mencapai penghalang, kami memeriksa apakah penghitung statis kami nol. jika tidak, kita menyetel utas ke tidur dengan fungsi tunggu variabel kondisi. ketika utas terakhir tiba di penghalang, nilai penghitung akan dikurangi menjadi nol dan utas terakhir ini akan memanggil fungsi sinyal variabel kondisi yang akan membangunkan semua utas lainnya!

Danielle
sumber
1

Saya mengajukan variabel kondisi di bawah sinkronisasi monitor. Saya biasanya melihat semaphores dan monitor sebagai dua gaya sinkronisasi yang berbeda. Ada perbedaan antara keduanya dalam hal seberapa banyak data status disimpan secara inheren dan bagaimana Anda ingin membuat kode model - tetapi sebenarnya tidak ada masalah yang dapat diselesaikan oleh satu tetapi tidak yang lain.

Saya cenderung membuat kode ke bentuk monitor; dalam sebagian besar bahasa yang saya gunakan, itu turun ke mutex, variabel kondisi, dan beberapa variabel status pendukung. Tapi semaphores akan melakukan pekerjaan itu juga.

Justin R.
sumber
2
Ini akan menjadi jawaban yang lebih baik jika Anda menjelaskan apa itu "bentuk monitor".
Steven Lu
0

The mutexdan conditional variablesdiwarisi dari semaphore.

  • Untuk mutex, semaphoremenggunakan dua status: 0, 1
  • Untuk condition variablesyang semaphore menggunakan counter.

Mereka seperti gula sintaksis

Aqua
sumber
Di pustaka C ++ std, mereka semua adalah objek distrik, semuanya diimplementasikan menggunakan API khusus platform. Tentu saja semaphore akan membuka blokir berapa kali diberi sinyal, variabel kondisi mungkin diberi sinyal beberapa kali tetapi hanya membuka blokir sekali. Inilah sebabnya mengapa wair menggunakan mutex sebagai parameter.
doron