sync.Cond.Wait melanjutkan eksekusi setelah notification, tetapi notification tidak menyatakan bahwa kondisi khusus milik caller masih true ketika goroutine memperoleh lock kembali. Shared predicate tetap menjadi source of truth, sehingga waiter memeriksanya lagi setiap kali Wait kembali.

Boundary ini memisahkan notification dari state. Signal dan Broadcast mengumumkan bahwa state yang relevan mungkin telah berubah; keduanya tidak memindahkan ownership state tersebut atau memesannya untuk waiter tertentu.

Wait membuka lock lalu memperolehnya kembali

Sebuah Cond terkait dengan Locker, umumnya *sync.Mutex. Caller memegang lock tersebut saat memeriksa shared state. Jika predicate false, Wait secara atomik membuka locker dan menangguhkan caller. Sebelum Wait kembali, ia mengunci locker lagi.

mu.Lock()
for len(queue) == 0 {
    cond.Wait()
}
item := queue[0]
queue = queue[1:]
mu.Unlock()

Loop adalah bagian dari synchronization contract di sekitar shared predicate. Setelah wakeup dan lock reacquisition, goroutine lain mungkin sudah mengubah state yang memicu notification tersebut.

Notification tidak memesan shared state

Misalkan beberapa goroutine menunggu queue menjadi tidak kosong. Producer menambahkan satu item lalu memberi signal kepada seorang waiter. Waiter yang dipilih menjadi runnable, tetapi tidak berjalan di luar kompetisi scheduler dan mutex yang normal.

Sebelum waiter tersebut memperoleh mutex kembali, goroutine lain dapat memperoleh mutex dan mengambil item itu. Ketika signaled waiter akhirnya kembali dari Wait, queue bisa sudah kosong lagi.

Signal tetap valid sebagai event: queue memang berubah. Namun signal bukan claim permanen atas item tersebut. Memperlakukan notification sebagai reservation dapat menyebabkan invalid read, stale assumption, atau operasi terhadap state yang sudah tidak lagi memenuhi predicate yang dibutuhkan.

Signal memilih satu waiter tanpa menetapkan application ownership

Signal membangunkan satu goroutine yang sedang menunggu condition, jika ada. Package ini tidak mengubah pemilihan tersebut menjadi ownership atas application data.

Goroutine yang dibangunkan tetap harus memperoleh associated lock kembali. Urutan lock acquisition bukan application-level handoff protocol, dan goroutine lain yang bersaing untuk mutex yang sama dapat berjalan lebih dahulu.

Untuk state yang merepresentasikan limited resource, protected predicate menentukan apakah resource tersebut masih tersedia. Waiter yang melihat predicate kembali false akan kembali ke Wait, bukan melanjutkan berdasarkan notification sebelumnya.

Broadcast menciptakan contention di sekitar satu perubahan state

Broadcast membangunkan semua waiter saat ini. Ini tepat ketika satu state transition dapat memungkinkan beberapa blocked goroutine maju, atau ketika masing-masing waiter memiliki predicate berbeda atas protected state yang sama.

Semua goroutine yang dibangunkan tetap harus berserial melalui locker sebelum memeriksa state. Jika hanya satu yang dapat mengonsumsi resource baru, goroutine lain dapat memperoleh lock, melihat predicate false, lalu menunggu lagi.

Karena itu broadcast dapat menciptakan burst goroutine runnable tanpa menjamin progress yang sama. Biayanya mencakup scheduler activity dan lock contention yang sebanding dengan waiting population, sehingga secara material berbeda dari memberi signal kepada satu waiter.

Predicate mutation dan notification termasuk dalam satu desain sinkronisasi

Shared state biasanya diubah sambil memegang lock yang sama dengan yang digunakan waiter. Ini memberi predicate inspection dan mutation synchronization boundary yang konsisten.

mu.Lock()
queue = append(queue, item)
cond.Signal()
mu.Unlock()

Notification dapat terjadi saat lock masih dipegang. Waiter yang dibangunkan tidak dapat kembali dari Wait sampai memperoleh lock itu lagi, sehingga ia mengamati state melalui mutex synchronization edge, bukan hanya melalui notification.

Memanggil Signal atau Broadcast sendiri tidak mewajibkan lock sedang dipegang, tetapi memisahkan state mutation dari locking discipline dapat membuat protocol rapuh. Properti pentingnya adalah waiter tidak boleh melewatkan state transition di antara pemeriksaan predicate dan saat mulai menunggu; Wait menyediakan operasi atomic unlock-and-suspend yang dibutuhkan untuk boundary tersebut.

Cond tidak menyimpan event history

Condition variable tidak mempertahankan notification untuk waiter di masa depan. Jika Signal berjalan ketika tidak ada goroutine yang menunggu, tidak ada token yang disimpan untuk pemanggilan Wait berikutnya.

Perilaku ini berbeda dari buffered channel atau semaphore-like counter. Future waiter harus memeriksa shared state saat ini, bukan menyimpulkan sesuatu dari notification sebelumnya.

Hal ini juga membuat pemeriksaan predicate sebelum Wait menjadi penting. Jika desired state sudah tersedia, goroutine langsung melanjutkan. Menunggu terlebih dahulu akan bergantung pada notification di masa depan yang mungkin tidak pernah datang walaupun state saat ini sudah memungkinkan progress.

Predicate mendefinisikan correctness

Cond mengoordinasikan goroutine di sekitar state yang dijaga di tempat lain. Condition variable tidak meng-encode queue length, capacity, lifecycle phase, generation number, atau application invariant lainnya.

Predicate yang berguna karena itu eksplisit dan dapat diuji di bawah associated lock. Contohnya termasuk len(queue) > 0, active < limit, atau generation != observedGeneration. Beberapa predicate dapat berbagi satu condition variable ketika perubahan state yang sama relevan bagi semuanya, walaupun broadcast yang lebih luas dapat meningkatkan unnecessary wakeup.

Durable contract berada pada protected state. Wait menyediakan transisi atomik dari locked inspection ke suspension lalu kembali ke locked inspection; Signal dan Broadcast menyediakan wakeup hint yang terkait dengan state change. Memeriksa ulang predicate menghubungkan mekanisme tersebut tanpa memperlakukan transient notification sebagai persistent state.