# C++ Conditional Variable 討論 - [RAII 機制](https://zh.wikipedia.org/zh-tw/RAII) - `unique_lock<mutex> ulocker(mu);` 會去要 `mu` 這把 lock,要不到的話會馬上 block 住直到要到為止才執行下一行,而要到的 lock 會在離開該 scope 之後自動 `lock.unlock()` - `cond.wait(ulocker);` - 可以拆成 `"unlock + wait" -> "wake up" -> "lock"` ,三件事分別 atomic - 因為 "unlock + wait" 是 atomic 因此不會造成 deadlock - 而被 notify 之後會去要 lock,也有可能當下要不到,但可以保證 Conditional Variable 的另一件事情已經做完 - `cond.wait(ulocker, condition);` - 先檢查 condition - 成立: 直接通過 - 不成立: "unlock + wait" -> "wake up" -> "lock" -> 再檢查一次 condition ```c= // 以下兩者等價 cond.wait(ulocker,[=](){return c==1;}); while(c!=1){ cond.wait(ulocker); } ``` - 如果 t2 只用 `cond.wait(ulocker);` ,不能控制流程,因為無法保證 t1 在執行 `notify_all()` 的時候,t2 已經要到 `mu` 這把 lock 並且執行 `cond.wait(ulocker);`,若還沒則之後 t2 會被 block 住 ```clike= mutex mu; condition_variable cond; void fun1(){ unique_lock<mutex> ulocker(mu); cout<<"111"<<endl; //mu.unlock(); cond.notify_all(); } void fun2(){ unique_lock<mutex> ulocker(mu); cond.wait(ulocker); cout<<"222"<<endl; } ``` - 只單純使用 `cond.wait(ulocker);` ,應該無法正確控制流程,底下有兩個可能解法 - 解法一: 另外使用全域變數 c,考慮 t2 取得 lock 的瞬間 - 如果 t1 已經執行完,則 `c == 1` ,t2 直接往下執行 - 如果 t1 都還沒執行,則 `c == 0` ,t2 先 `lock.unlock()` 等候通知,這時候 f1 才有機會進入 critical section 執行 `cond.notify_all();` - 其實 `while` 改成 `if` 也會對,但是 SPEC 說 `"wait for notify"` 的狀態可能會出現 [`spurious wake-up calls`](https://en.wikipedia.org/wiki/Spurious_wakeup) 而突然被 notify,因此用 `while` 迴圈檢查 t1 真的做完事情 ```clike= int c = 0; mutex mu; condition_variable cond; void fun1(){ unique_lock<mutex> ulocker(mu); cout<<"111"<<endl; c = 1; cond.notify_all(); } void fun2(){ unique_lock<mutex> ulocker(mu); //cond.wait(ulocker,[=](){return c==1;}); 和底下等價 while(c!=1){ cond.wait(ulocker); } cout<<"222"<<endl; } ``` - 解法二: 讓 t1 執行 `lock.unlock()` 並睡 0.05 秒才通知 t2,有很大的可能性 t1 在 `cond.notify_all();` 時, t2 已經執行 `cond.wait(ulocker);`,因此程式可以順利控制流程 - 缺點是雙方都浪費時間,實務上也不知道要睡多久 ```clike= void fun1(){ unique_lock<mutex> ulocker(mu); cout<<"111"<<endl; mu.unlock(); Sleep(50); cond.notify_all(); } void fun2(){ unique_lock<mutex> ulocker(mu); cond.wait(ulocker); cout<<"222"<<endl; } ``` - 嘗試解三: 對 condition variable 不熟的時候可以用 busy waiting - 使用 volatile 避免編譯器最佳化去掉 while 迴圈 - 缺點: 程式會佔去絕大多數的 CPU 時間 ```clike= volatile int c = 0; void fun1(){ cout<<"111"<<endl; c = 1; } void fun2(){ while(c==0){ } cout<<"222"<<endl; } ``` - 討論 - 一、原本以為解法一也是 busy waiting ,但其實沒有 - 二、原本以為 `cond.wait(ulocker);` 就能控制流程,但至少要搭配全域變數 `c` ###### tags: `筆記` `C++` `Conditional Variable`
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up