要點
-
鎖+雙重判斷的技法
-
竟態(tài)條件:多線程程序執(zhí)行的結果一致,不會隨著CPU對線程不同的調用順序
線程間安全實例——3個窗口同時賣票
線程不安全的代碼如下
int ticketCount = 100; // 100張車票
// 模擬10個窗口同時賣票
void sellTicket(int index)
{
while (ticketCount > 0)
{
//cout << "窗口:" << index << "賣出第:" << ticketCount << "張票" << endl;
cout << ticketCount << endl; // 打印當前剩余票數(shù)
ticketCount--;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int main()
{
list<std::thread> tlist;
for (int i = 1; i <= 3; ++i)
{
tlist.push_back(std::thread(sellTicket, i));
}
for (std::thread& t : tlist)
{
t.join();
}
cout << "所有窗口賣票結束!" << endl;
return 0;
}
輸出的部分結果里有很多重復的數(shù)字,相當于同一張票被賣出多次,原因在于
ticketCount–; 是線程不安全的,理由如下
某一時刻ticketCount = 99,thread1此時調用ticketCount–,執(zhí)行到sub eax時執(zhí)行線程切換到另一個線程thread2中,此時ticketCount仍為99,執(zhí)行完3條匯編后ticketCount = 98,此時再切換回thread1,繼續(xù)執(zhí)行完后面匯編,也使ticketCount = 98,這里就導致同時輸出兩次98;
解決方法
保證某一線程ticketCount–沒做完,其他線程不允許做ticketCount–操作,可以使用加mutex互斥鎖的方法:
void sellTicket(int index)
{
mtx.lock();
while (ticketCount > 0)
{
//cout << "窗口:" << index << "賣出第:" << ticketCount << "張票" << endl;
cout << ticketCount << endl; // 打印當前剩余票數(shù)
ticketCount--;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
mtx.unlock();
}
但是這樣的加鎖問題在于鎖粒度太大,可以進一步縮小,采用鎖+雙重判斷的方法:
void sellTicket(int index)
{
while (ticketCount > 0)
{
mtx.lock();
if (ticketCount > 0) // !!!
{
cout << "窗口:" << index << "賣出第:" << ticketCount << "張票" << endl;
//cout << ticketCount << endl; // 打印當前剩余票數(shù)
ticketCount--;
}
mtx.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
這里if (ticketCount
> 0) 判斷必須加,如果不加,當ticketCount = 1時,切換到其他線程賣完后ticketCount = 0 ,切換回原線程繼續(xù)賣票就變成可以賣第0張票,不合法
lock_guard和unique_lock
lock_guard
lock_guard
不能用在函數(shù)參數(shù)傳遞或返回過程中,只能用在簡單的臨界區(qū)代碼段互斥操作;
類似于scoped_str
;
lock_guard
是對std::mutex
的封裝,拷貝構造和賦值函數(shù)被delete
,它是RAII
技術的實踐,創(chuàng)建對象時就加鎖,出作用域析構調用解鎖,用lock_guard
替換上面案例的mutex
:
void sellTicket(int index)
{
while (ticketCount > 0)
{
//mtx.lock();
{
lock_guard<std::mutex> lock(mtx); //
if (ticketCount > 0)
{
cout << "窗口:" << index << "賣出第:" << ticketCount << "張票" << endl;
ticketCount--;
}
}
//mtx.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
unique_lock
unique_lock
不僅可用在函數(shù)參數(shù)傳遞或返回過程中,還能用在函數(shù)調用中,如 和條件變量函數(shù)一起使用:文章來源:http://www.zghlxwxcb.cn/news/detail-438224.html
unique_lock<std::mutex> lck(mtx);
cv.wait(lck); // => #1.使線程進入等待狀態(tài) #2.lck.unlock可以把mtx給釋放掉
unique_lock
也是對mutex
的封裝,它也可以像lock_guard
一樣使用,同時它支持手動調用lock
和unlock
,會幫助檢查是否忘記調用unlock
,使用例子如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-438224.html
void sellTicket(int index)
{
while (ticketCount > 0)
{
{
unique_lock<std::mutex> lck(mtx);
lck.lock();
if (ticketCount > 0)
{
cout << "窗口:" << index << "賣出第:" << ticketCount << "張票" << endl;
ticketCount--;
}
lck.unlock();
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
到了這里,關于線程間互斥-mutex互斥鎖和lock_guard的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!