[英]test_and_set makes the thread deadlock
#include<bits/stdc++.h>
using namespace std;
int cnt=0;
int locked=0;
int test_and_set(int * lock){
int temp=*lock;
*lock=1;
return temp;
}
void cntOnes(int t){
while(test_and_set(&locked));
for(int i=0;i<2000;i++){
cnt++;
cout<<t<<" -> "<<cnt<<endl;
}
locked=0;
}
int main(){
thread t1(cntOnes,1);
thread t2(cntOnes,2);
t1.join();
t2.join();
}
我使用 test_and_set 方法讓其他線程首先等待,線程 t1 可以中斷 while 循環,但即使在線程 t1 將值設置為 0 之后。線程 t2 繼續運行 while 循環,它不會中斷 while 循環。 應該做哪些改變?
首先, 為什么我不應該#include <bits/stdc++.h>?
不幸的是,除此之外,您的整個代碼都是一場偉大的大數據競賽。 如果不使用互斥鎖或原子來同步它們,您根本無法使用普通變量在線程之間進行通信。 在兩個不同的線程中同時讀寫一個普通變量是數據競爭的定義,C++ 標准說數據競爭會導致未定義的行為(= makes your program completely broken)。
可以go錯的東西很多:
由於數據競爭規則,編譯器假設一個普通變量不會自發地改變值,除非被這個線程寫入。 因此它認為while(test_and_set(&locked));
,如果第一次不成功,以后就永遠不會成功,還不如優化成死循環。 (或者由於無限循環也是 UB,它可能會被完全刪除。)
編譯器同樣假設沒有其他線程正在查看我們修改的普通變量。 因此,例如,由於它觀察到運行cntOnes
的線程最終不可避免地會設置locked = 0
,因此它可以決定在 2000 步循環之前執行此操作。
您的test_and_set
是原子的。 即使加載和存儲本身是原子的和順序一致的(它們不是),操作也可以按如下方式交錯:
thread 1 thread 2
======== ========
temp = *lock; // temp = 0
temp = *lock; // temp = 0
*lock = 1;
*lock = 1;
return temp; // return 0 return temp; // return 0
現在兩個線程都認為他們持有鎖。 哦親愛的。
所以你不能用普通的int
變量實現正確的“鎖定”。 看起來您正在嘗試重新發明std::mutex
,那么為什么不直接使用它呢?
#include <thread>
#include <iostream>
#include <mutex>
int cnt = 0;
std::mutex my_lock;
void cntOnes(int t) {
my_lock.lock();
for(int i=0; i<2000; i++) {
cnt++;
std::cout << t << " -> " << cnt << std::endl;
}
my_lock.unlock();
}
int main() {
std::thread t1(cntOnes,1);
std::thread t2(cntOnes,2);
t1.join();
t2.join();
}
或者更好的是,用std::scoped_lock
做 RAII 風格:
void cntOnes(int t) {
std::scoped_lock<std::mutex> guard(my_lock);
for(int i=0; i<2000; i++) {
cnt++;
std::cout << t << " -> " << cnt << std::endl;
}
}
這里guard
的構造函數獲取互斥量,而析構函數釋放它。 在 C++17 之前,您可以改用std::lock_guard
,只要您只使用一個互斥量即可。
如果你堅持自己做測試和設置,那么你必須使用原子,例如std::atomic<bool>
或std::atomic_flag
。 這很難做到正確,特別是如果你想用任何比std::memory_order_seq_cst
弱的東西優化 memory 排序; 它需要完全理解 C++ memory model 這非常抽象。 所以我會等到您可以舒適地使用互斥鎖后再嘗試這種方法。
也可以看看:
如果您認為volatile int
可能會解決您的問題,它不會:請參閱何時將 volatile 與多線程一起使用?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.