5.2.25 · D2 · HinglishC++ Programming

Visual walkthroughstd - condition_variable

2,206 words10 min read↑ Read in English

5.2.25 · D2 · Coding › C++ Programming › std - condition_variable


Step 0 — Teen words jo hum use karenge (pehle vocabulary earn karo)

Kisi bhi diagram se pehle, teen plain-word definitions. Neeche koi bhi aisa word use nahi hoga jo yahan define na ho.

Apne dimaag mein yeh char pictures rakho: worker, key, waiting-room-with-doorbell, shared box. Baaki sab yahi char cheezein move karti hain.


Step 1 — Naive attempt, aur kyun yeh jalti hai

KYA. Consumer queue se ek number chahta hai. Pehla idea jo koi beginner likhta hai:

while (queue.empty()) { /* keep looping */ }   // busy-wait

KYun tempting lagta hai. Yeh literally English sentence hai "jab tak empty hai, check karte raho." Yeh kaam karta hai.

KYun galat hai. Picture dekho. Consumer empty() ko ek second mein lakho baar check kar raha hai. Har check kuch useful nahi karta — box abhi bhi empty hai — lekin CPU 100% pe pinned hai. Ek core wali machine pe, consumer kabhi producer ko run nahi karne deta, toh box hamesha empty rehta hai. Isse busy-wait kehte hain, aur yahi woh bimari hai jise hum theek karne wale hain.

Figure — std - condition_variable

Step 2 — Shared box, ek key se guarded

KYA. Hum shared state aur uska guard setup karte hain:

std::queue<int> q;          // numbers ka shared box
std::mutex      m;          // q ko touch karne ki EK key
std::condition_variable cv; // waiting room + doorbell
bool done = false;          // "producer khatam ho gaya" flag

KYon. Do workers ko kabhi bhi q ek hi instant mein touch nahi karna chahiye — yeh ek Race Condition hai. Rule: q ya done touch karne se pehle key m hold karo. cv alag hai: yeh kuch protect nahi karta, yeh sirf sleepers ko park aur wake karta hai.

PICTURE. Box q ek locked room ke andar hai. Single key m ek hook pe lagi hai. Side mein cv ka waiting room hai apni doorbell ke saath. Abhi koi key hold nahi kar raha.

Figure — std - condition_variable

Step 3 — Consumer key leta hai, phir box empty milta hai

KYA.

std::unique_lock<std::mutex> lk(m);          // key pakdo
cv.wait(lk, []{ return !q.empty() || done; }); // check karo, shayad so jao

Aao us doosri line ke pieces ko term by term name karte hain:

KYon unique_lock aur lock_guard nahi? Ek step mein wait ko key chodna padta hai (taaki producer use pakad sake) aur baad mein wapas lena padta hai. Yeh hai unlock-then-relock — ek std::unique_lock dono kar sakta hai; ek std::lock_guard sirf ek baar lock kar sakta hai aur kabhi nahi chhod sakta. Isliye waiter ko zaroor unique_lock use karna chahiye.

KYon predicate P()? P() woh honest sawaal hai "kya mujhe jaag rahe rehna chahiye?" Abhi box empty hai aur done false hai, toh P() false hai. Kyunki P() false hai, wait decide karta hai: jaag kar rehne ka koi fayda nahi → yeh key release karta hai aur so jaata hai.

PICTURE. Consumer key hold karta hai, box mein jhankta hai, empty pata hai. P() amber mein false dikhta hai.

Figure — std - condition_variable

Step 4 — Atomic magic: wait key drop karta hai aur so jaata hai

KYA. Kyunki P() false tha, wait(lk) teen cheezein ek indivisible move mein karta hai:

KYon "atomic" matter karta hai. "P() check karo" aur "so jao" ke beech koi gap nahi hona chahiye. Agar gap hota, producer us gap mein doorbell baja sakta tha — aur consumer, abhi tak soya nahi, use miss kar deta aur hamesha ke liye so jaata. Yeh lost wakeup race hai (iske opposite problem ke liye Spurious Wakeup bhi dekho). Standard promise karta hai: key release karna aur sona shuru karna saath hote hain, toh notify kabhi kisi crack mein nahi ghus sakta.

PICTURE. Consumer ka chehra ab waiting room mein soya hua hai. Key m wapas hook pe hai — producer ke liye free. CPU meter 0% dikhata hai.

Figure — std - condition_variable

Step 5 — Producer kaam karta hai aur doorbell bajata hai

KYA.

{
    std::lock_guard<std::mutex> lk(m);  // free key pakdo
    q.push(0);                          // box mein ek number daalo
}                                       // key YAHAN release hoti hai (scope khatam)
cv.notify_one();                        // doorbell bajao

KYon producer lock_guard use kar sakta hai. Producer ko sirf lock, mutate, ek baar unlock karna hai. Yeh kabhi nahi sota, toh ise kabhi unlock/relock nahi chahiye — simpler std::lock_guard yahan correct hai.

KYon notify karne se pehle key release karo? Agar hum key hold karte hue bell bajate hain, toh jaga hua consumer turant key pakdne ki koshish karta hai, use liya hua paata hai, aur firse block ho jaata hai — ek bekar bounce. Pehle release karne ka matlab hai consumer ko available key milti hai jaagne ke baad. (Yeh ek optimization hai; lock ke andar notify karna bhi correct hai.)

PICTURE. Producer key hold karta hai, box mein 0 dalta hai, key wapas latkata hai, phir amber doorbell dabaata hai. Ek wake-signal waiting room ki taraf jaata hai.

Figure — std - condition_variable

Step 6 — Consumer jaagta hai, dobara check karta hai, aur tabhi kaam karta hai

KYA. Doorbell sleeper ko jagaati hai. wait ab key re-lock karta hai aur P() dobara evaluate karta hai: Toh wait return karta hai, consumer key hold karta hai, aur box drain karta hai.

KYon dobara check karo — kya bell ka matlab "jao" nahi tha? Nahi. Do cheezein aapko jhooth bol sakti hain:

  1. Ek spurious wakeup — OS ek sleeper ko bina kisi ke bell bajaye jagaa sakta hai. Dekho Spurious Wakeup.
  2. Ek stolen wakeup — kai consumers ke saath, doosra consumer single item pehle grab kar sakta hai, is consumer ke liye box empty chhodke.

Predicate loop safety net hai. Formally predicate overload ek loop hi hai: Term by term: while P() abhi bhi false hai, wapas so jao; sirf tab niklo jab P() genuinely true ho. Isliye hum yahan kabhi if use nahi karte.

PICTURE. Consumer jaag gaya, haath mein key hai, P() ab green/true hai, box se 0 nikaal raha hai.

Figure — std - condition_variable

Step 7 — Edge cases jo aap skip nahi kar sakte

Real code corners se milta hai. Yeh sab hain, har ek ke liye apna frame.

Case A — Spurious wakeup (kisi ne bell nahi bajaayi). Consumer khud jaag jaata hai. Yeh re-lock karta hai, P() = false evaluate karta hai (box abhi bhi empty hai), aur while use seedha wapas so bhejna deta hai. Koi nuksan nahi — kyunki humne loop kiya.

Case B — Notify jab koi wait nahi kar raha (lost notify). Ek condition_variable ki koi memory nahi hoti. Agar producer bell bajata hai jab consumer abhi waiting room mein gaya nahi hai, toh woh ring void mein ghum jaati hai. Yeh theek hai sirf isliye kyunki number already box mein rakha hua hai — jab consumer finally P() check karta hai, toh box (shared flag) sach bolta hai. Lesson: correctness shared state mein rehti hai, ring mein nahi.

Case C — Shutdown (done = true, notify_all). Jab producer khatam ho jaata hai:

{ std::lock_guard<std::mutex> lk(m); done = true; }
cv.notify_all();   // HAR sleeper ko jagao taaki woh exit kar sakein

notify_all sab consumers ko jagaata hai; har ek done se P() true dekhta hai, kuch bacha ho toh drain karta hai, aur exit karta hai. Yahan notify_one use karne se doosre consumers hamesha ke liye sote reh sakte hain — ek Deadlock-jaisi hang.

Figure — std - condition_variable

Ek-picture summary

Yeh single frame Steps 2–7 compress karta hai: producer aur consumer ke beech key ka pass hona, sleep-with-key-released move, doorbell, aur re-check loop.

Figure — std - condition_variable
Recall Feynman: poori story ek 12-saal ke bachhe ko batao

Do bachche ek locked room mein ek toy box share karte hain, aur exactly ek key hai. Dene wala bachcha toys dalta hai; lene wala bachcha unhe nikalta hai. Lene wala key pakadta hai, andar jhaankta hai — empty. Yahan khade rehke ghoorte rehne ki jagah (yeh use bina kisi kaam ke thaka dega, yahi busy-wait hai), woh key wapas latkata hai aur ek nap-room mein so jaata hai jahan bell hai — aur "key latkaana + so jaana" woh ek smooth motion mein karta hai taaki kuch bhi slip na ho sake. Ab dene wala key pakad sakta hai, ek toy daal sakta hai, key wapas lataak sakta hai, aur bell baja sakta hai. Lene wala jaagta hai, key dobara pakadta hai, aur — important — yakin karne se pehle dobara box mein dekhta hai, kyunki kabhi kabhi bell galti se bajti hai, ya kisi doosre lene wale ne toy pehle le liya. Agar box mein sach mein kuch hai, toh woh khelta hai; warna woh fir so jaata hai. Jab dene wala sab khatam kar leta hai, woh box mein "hum done hain" ka note rakhta hai aur sabke liye bell bajata hai, taaki koi bhi napper hamesha ke liye soya na rahe. Beech mein jo magic trick hai woh yeh hai: key chodna aur ek exact instant mein so jaana — yahi ek condition_variable karta hai.

Recall

cv.wait ko unique_lock kyun lena chahiye, lock_guard nahi? ::: Kyunki wait ko sone se pehle mutex unlock karna padta hai aur jaagne par re-lock karna padta hai; unique_lock repeated unlock/relock support karta hai, lock_guard nahi kar sakta. Wake-up check while loop kyun hai, if nahi? ::: Spurious wakeups aur stolen wakeups se bachne ke liye — thread ko real predicate dobara verify karna chahiye aur agar abhi bhi false hai toh wapas so jaana chahiye. Correctness actually kahaan rehti hai — notify mein, ya shared flag mein? ::: Shared flag/state mein; ek condition_variable ki koi memory nahi hoti, isliye koi waiter na ho tab notify lost ho jaati hai — flag sach carry karta hai.


🇮🇳 Yeh note Hinglish mein padho → · See also Producer-Consumer Pattern, std::future and std::promise, std::atomic.