C++詳解如何實(shí)現(xiàn)兩個(gè)線程交替打印
首先簡單搭一個(gè)框架,讓兩個(gè)線程先嘗試實(shí)現(xiàn)交替打印。
//實(shí)現(xiàn)兩個(gè)線程交替打印
#include <iostream>
#include <thread>
using namespace std;
int main(void)
{
int n = 100;
int i = 0;
//創(chuàng)建兩個(gè)線程
thread t1([&n, &i](){
while (i < n)
{
cout << i << " ";
i++;
}
});
thread t2([&n, &i]() {
while (i < n)
{
cout << i << " ";
i++;
}
});
if (t1.joinable())
{
t1.join();
}
if (t2.joinable())
{
t2.join();
}
return 0;
}為了讓我們更加清楚是哪個(gè)線程打印了,我們需要獲取線程的ID。
#include <iostream>
#include <thread>
using namespace std;
int main(void)
{
int n = 100;
int i = 0;
//創(chuàng)建兩個(gè)線程
thread t1([&n, &i](){
while (i < n)
{
cout << this_thread::get_id() << ": " << i << endl;
i++;
}
});
thread t2([&n, &i]() {
while (i < n)
{
cout << this_thread::get_id() << ": " << i << endl;
i++;
}
});
if (t1.joinable())
{
t1.join();
}
if (t2.joinable())
{
t2.join();
}
return 0;
}
這顯然沒有完成兩個(gè)線程交替打印的目的,甚至數(shù)據(jù)的打印都非常地亂。這是因?yàn)閕是臨界資源,多個(gè)線程爭搶訪問臨界資源可能會造成數(shù)據(jù)二義,線程是不安全的,需要保證任意時(shí)刻只有一個(gè)線程能夠訪問臨界資源。
所以創(chuàng)建一個(gè)互斥量,并在臨界區(qū)合適的地方加鎖和解鎖。由于線程的執(zhí)行函數(shù)我使用了lambda表達(dá)式,為了讓兩個(gè)線程使用的是同一把鎖,把鎖創(chuàng)建在了main函數(shù)內(nèi),并在lambda表達(dá)式內(nèi)使用了引用捕捉。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int main(void)
{
int n = 100;
int i = 0;
mutex mtx;
//創(chuàng)建兩個(gè)線程
thread t1([&n, &i, &mtx](){
while (i < n)
{
mtx.lock();
cout << this_thread::get_id() << ": " << i << endl;
i++;
mtx.unlock();
}
});
thread t2([&n, &i, &mtx]() {
while (i < n)
{
mtx.lock();
cout << this_thread::get_id() << ": " << i << endl;
i++;
mtx.unlock();
}
});
if (t1.joinable())
{
t1.join();
}
if (t2.joinable())
{
t2.join();
}
return 0;
}在C++中,一般不直接操作鎖,而是由類去管理鎖。
//第一個(gè)管理鎖的類 template <class Mutex> class lock_guard; //第二個(gè)管理鎖的類 template <class Mutex> class unique_lock;
lock_guar類,只有構(gòu)造和析構(gòu)函數(shù)。一般用于加鎖和解鎖,這里進(jìn)行簡單的模擬:
//注意:為了使得加鎖和解鎖的是同一把鎖
//需要使用引用
template <class Lock>
class LockGuard
{
public:
LockGuard(Lock &lck)
:_lock(lck)
{
_lock.lock();
}
~LockGuard()
{
_lock.unlock();
}
private:
Lock &_lock;
};unique_lock的成員方法就不僅僅是析構(gòu)函數(shù)和構(gòu)造函數(shù)。詳見文檔unique_lock介紹和使用。
這里將鎖交給unique_lock類的對象進(jìn)行管理。
int main(void)
{
int n = 100;
int i = 0;
mutex mtx;
//創(chuàng)建兩個(gè)線程
thread t1([&n, &i, &mtx, &cv, &flag](){
while (i < n)
{
unique_lock<mutex> LockManage(mtx);
cout << this_thread::get_id() << ": " << i << endl;
i++;
}
});
thread t2([&n, &i, &mtx, &cv, &flag]() {
while (i < n)
{
unique_lock<mutex> LockManage(mtx);
cout << this_thread::get_id() << ": " << i << endl;
i++;
}
});
if (t1.joinable())
{
t1.join();
}
if (t2.joinable())
{
t2.join();
}
return 0;
}
線程是安全了,但如果其中一個(gè)線程競爭鎖的能力比較強(qiáng),那么可能會出現(xiàn)上面這種情況。
需要控制:一個(gè)線程執(zhí)行一次后,如果再次去執(zhí)行就不準(zhǔn)許了,同時(shí)可以喚醒另一個(gè)進(jìn)程去執(zhí)行,如此循環(huán)往復(fù)達(dá)到交替打印的目的。所以可以增加一個(gè)條件變量,讓某個(gè)線程在該條件變量下的阻塞隊(duì)列等待。
C++庫中線程在條件變量下的等待函數(shù)第一個(gè)參數(shù)注意是管理鎖的類對象
int main(void)
{
int n = 100;
int i = 0;
mutex mtx;
condition_variable cv;
bool flag = false;
//創(chuàng)建兩個(gè)線程
thread t1([&n, &i, &mtx, &cv, &flag](){
while (i < n)
{
unique_lock<mutex> LockManage(mtx);
//!flag為真,那么獲取后不會阻塞,優(yōu)先運(yùn)行
cv.wait(LockManage, [&flag]() {return !flag; });
cout << this_thread::get_id() << ": " << i << endl;
i++;
}
});
thread t2([&n, &i, &mtx, &cv, &flag]() {
while (i < n)
{
unique_lock<mutex> LockManage(mtx);
//flag為假,競爭到鎖后,由于條件不滿足,阻塞
cv.wait(LockManage, [&flag]() {return flag; });
cout << this_thread::get_id() << ": " << i << endl;
i++;
}
});
if (t1.joinable())
{
t1.join();
}
if (t2.joinable())
{
t2.join();
}
return 0;
}這里flag以及l(fā)ambda表達(dá)式的增加是非常巧妙的。flag的初始化值為false,讓線程t2在[&flag]() {return false; }下等待,那么t2線程就會先執(zhí)行。

線程t1競爭到了鎖,但是由于不滿足條件,會繼續(xù)等待,所以就出現(xiàn)了上面的情況。
需要一個(gè)線程喚醒另一個(gè)線程之前,將flag的值進(jìn)行修改。
int main(void)
{
int n = 100;
int i = 0;
mutex mtx;
condition_variable cv;
bool flag = false;
//創(chuàng)建兩個(gè)線程
thread t1([&n, &i, &mtx, &cv, &flag](){
while (i < n)
{
unique_lock<mutex> LockManage(mtx);
//!flag為真,那么獲取后不會阻塞,優(yōu)先運(yùn)行
cv.wait(LockManage, [&flag]() {return !flag; });
cout << this_thread::get_id() << ": " << i << endl;
i++;
flag = true;
cv.notify_one();
}
});
thread t2([&n, &i, &mtx, &cv, &flag]() {
while (i < n)
{
unique_lock<mutex> LockManage(mtx);
//flag為假,競爭到鎖后,由于條件不滿足,阻塞
cv.wait(LockManage, [&flag]() {return flag; });
cout << this_thread::get_id() << ": " << i << endl;
i++;
flag = false;
cv.notify_one();
}
});
if (t1.joinable())
{
t1.join();
}
if (t2.joinable())
{
t2.join();
}
return 0;
}最終,實(shí)現(xiàn)了兩個(gè)線程交替打?。ㄒ粋€(gè)線程打印奇數(shù)、一個(gè)線程打印偶數(shù))

到此這篇關(guān)于C++詳解如何實(shí)現(xiàn)兩個(gè)線程交替打印的文章就介紹到這了,更多相關(guān)C++線程交替打印內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于AVLTree(C++實(shí)現(xiàn))沒有統(tǒng)一旋轉(zhuǎn)操作的問題
這篇文章主要介紹了關(guān)于AVLTree(C++實(shí)現(xiàn))沒有統(tǒng)一旋轉(zhuǎn)操作的問題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-02-02
使用C語言實(shí)現(xiàn)繪制立體分離式環(huán)圖
這篇文章主要為大家詳細(xì)介紹了使用C語言實(shí)現(xiàn)繪制立體分離式環(huán)圖的相關(guān)知識,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03
C++學(xué)校運(yùn)動會管理系統(tǒng)的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了C++如何實(shí)現(xiàn)學(xué)校運(yùn)動會管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10
C語言調(diào)用攝像頭實(shí)現(xiàn)生成yuv未壓縮圖片
這篇文章主要為大家詳細(xì)介紹了C語言如何調(diào)用攝像頭實(shí)現(xiàn)生成yuv未壓縮圖片,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以參考一下2023-11-11
Visual Studio 2019配置OpenCV4.1.1詳細(xì)圖解教程
這篇文章主要介紹了Visual Studio 2019配置OpenCV4.1.1詳細(xì)圖解教程 ,需要的朋友可以參考下2020-02-02
C++ 繼承,虛繼承(內(nèi)存結(jié)構(gòu))詳解
C++繼承和虛繼承的內(nèi)存模型是一個(gè)老生常談的話題,實(shí)現(xiàn)方法主要依賴于編譯器,本文從多個(gè)角度通過代碼詳解C++中虛繼承的內(nèi)存模型知識,感興趣的朋友跟隨小編一起看看吧2021-09-09
C++利用靜態(tài)成員或類模板構(gòu)建鏈表的方法講解
這篇文章主要介紹了C++利用靜態(tài)成員或類模板構(gòu)建鏈表的方法講解,鏈表是基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu),而在C++中構(gòu)件單鏈表還是稍顯復(fù)雜,需要的朋友可以參考下2016-04-04

