欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++詳細(xì)分析線程間的同步通信

 更新時(shí)間:2022年05月04日 09:26:45   作者:liufeng2023  
線程間不通信的話,每個(gè)線程受CPU的調(diào)度,沒有任何執(zhí)行上的順序可言,線程1和線程2是根據(jù)CPU調(diào)度算法來(lái)的,兩個(gè)線程都有可能先運(yùn)行,是不確定的,線程間的運(yùn)行順序是不確定的,所以多線程程序出問題,難以復(fù)現(xiàn),本章我們就來(lái)了解線程間的同步通信

1、多線程編程兩個(gè)問題

1.1、線程間的互斥

競(jìng)態(tài)條件: 多線程執(zhí)行的結(jié)果是一致的,不會(huì)隨著CPU對(duì)線程不同的調(diào)用順序,而產(chǎn)生不同的運(yùn)行結(jié)果。

發(fā)生競(jìng)態(tài)條件的代碼段,稱為臨界區(qū)代碼段(只有一個(gè)線程可以進(jìn)來(lái)),保證臨界區(qū)代碼段原子操作,通過線程互斥鎖mutex,也可以使用輕量級(jí)的無(wú)鎖實(shí)現(xiàn)CAS。

C++11的mutex底層實(shí)現(xiàn):

使用strace ./a.out跟蹤代碼,使用C++11提供的mutex,Linux底層使用的也是自己的pthread_mutex互斥鎖。

1.2、線程間的同步通信

  • 線程間不通信的話,每個(gè)線程受CPU的調(diào)度,沒有任何執(zhí)行上的順序可言,線程1和線程2是根據(jù)CPU調(diào)度算法來(lái)的,兩個(gè)線程都有可能先運(yùn)行,是不確定的,線程間的運(yùn)行順序是不確定的;
  • 所以多線程程序出問題,難以復(fù)現(xiàn),因?yàn)檎l(shuí)也不知道當(dāng)時(shí)線程執(zhí)行的先后順序,我們一般可以得到每個(gè)線程的線程棧信息來(lái)分析是否發(fā)生死鎖的問題之類的。
  • 我們要保證線程間的運(yùn)行順序

通信就是:

  • 線程1和線程2一起運(yùn)行,線程2要做的事情必須先依賴于線程1完成部分的事情,然后告訴線程2這部分東西做好了,線程2就可以繼續(xù)向下執(zhí)行了。
  • 或者是線程1接下來(lái)要做某些操作,這些操作需要線程2把另外一部分事情做完,然后通知一下線程1它做完了,然后線程1才能做這些操作。

生產(chǎn)者,消費(fèi)者線程模型

2、生產(chǎn)者-消費(fèi)者線程模型

注意: C++ STL所有的容器都不是線程安全的,都需要進(jìn)行封裝。

如果直接使用queue,使用queue的push和pop操作時(shí),會(huì)涉及線程安全問題。我們直接在queue的基礎(chǔ)上,直接將其封裝成線程安全的queue。

**注意:**線程函數(shù)代碼和后面的main函數(shù)代碼都是不會(huì)變的;

在這里插入圖片描述

使用lock_gard,不用直接使用互斥鎖的lock和unlock方法,通過棧上的對(duì)象構(gòu)造和出作用域析構(gòu),來(lái)自動(dòng)調(diào)用互斥鎖的lock和unlock方法;

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

封裝的queue代碼,為什么每次出錯(cuò)都是不一樣的?

消費(fèi)者消費(fèi)的比較快,queue為空時(shí),就會(huì)出錯(cuò);(需要有線程間的通信機(jī)制)

我們需要:生產(chǎn)者生產(chǎn)一個(gè)物品,通知消費(fèi)者消費(fèi)一個(gè);消費(fèi)完了,消費(fèi)者再通知生產(chǎn)者繼續(xù)生產(chǎn)物品

條件變量: 可以精確做線程間的同步通信;

信號(hào)量也可以做,但是做不到生產(chǎn)一個(gè)消費(fèi)一個(gè),這么精確。

在這里插入圖片描述

注意: 條件變量cv.wait里面?zhèn)鞯氖莡nique_lock,也只能傳unique_lock,因?yàn)閘ock_gard將拷貝構(gòu)造和賦值重載都delete了(實(shí)參到形參是一個(gè)拷貝構(gòu)造的過程,傳不進(jìn)來(lái))

#include <iostream>
#include <thread>//多線程的頭文件 
#include <mutex>//互斥鎖的頭文件 
#include <condition_variable>//條件變量的頭文件 
#include <queue>//C++ STL所有的容器都不是線程安全
using namespace std;
std::mutex mtx;//定義互斥鎖,做線程間的互斥操作
std::condition_variable cv;//定義條件變量,做線程間的同步通信操作
//生產(chǎn)者生產(chǎn)一個(gè)物品,通知消費(fèi)者消費(fèi)一個(gè);消費(fèi)完了,消費(fèi)者再通知生產(chǎn)者繼續(xù)生產(chǎn)物品
class Queue  
{
public:
	void put(int val)//生產(chǎn)物品
	{
		unique_lock<std::mutex> lck(mtx);//unique_ptr
		while (!que.empty())
		{
			//que不為空,生產(chǎn)者應(yīng)該通知消費(fèi)者去消費(fèi),消費(fèi)者消費(fèi)完了,生產(chǎn)者再繼續(xù)生產(chǎn)
			//生產(chǎn)者線程進(jìn)入#1等待狀態(tài),并且#2把mtx互斥鎖釋放掉
			cv.wait(lck);//傳入一個(gè)互斥鎖,當(dāng)前線程掛起,處于等待狀態(tài),并且釋放當(dāng)前鎖 lck.lock()  lck.unlock
		}
		que.push(val);
		/*
		notify_one:通知喚醒另外的一個(gè)線程的
		notify_all:通知喚醒其它所有線程的
		通知其它所有的線程,我生產(chǎn)了一個(gè)物品,你們趕緊消費(fèi)吧
		其它線程得到該通知,就會(huì)從等待狀態(tài) =》 到阻塞狀態(tài) =》 但是要獲取互斥鎖才能繼續(xù)向下執(zhí)行
		*/
		cv.notify_all();
		cout << "生產(chǎn)者 生產(chǎn):" << val << "號(hào)物品" << endl;
	}
	int get()//消費(fèi)物品
	{
		lock_guard<std::mutex> guard(mtx);//相當(dāng)于scoped_ptr
		unique_lock<std::mutex> lck(mtx);//相當(dāng)于unique_ptr 更安全 
		while (que.empty())
		{
			//消費(fèi)者線程發(fā)現(xiàn)que是空的,通知生產(chǎn)者線程先生產(chǎn)物品
			//#1 掛起,進(jìn)入等待狀態(tài) #2 把互斥鎖mutex釋放
			cv.wait(lck);
		}//如果其他線程執(zhí)行notify了,當(dāng)前線程就會(huì)從等待狀態(tài) =》到阻塞狀態(tài) =》但是要獲取互斥鎖才能繼續(xù)向下執(zhí)行 
		int val = que.front();
		que.pop();
		cv.notify_all();//通知其它線程我消費(fèi)完了,趕緊生產(chǎn)吧
		cout << "消費(fèi)者 消費(fèi):" << val << "號(hào)物品" << endl;
		return val;
	}
private:
	queue<int> que;
};
//這里模擬生產(chǎn)者生產(chǎn)10個(gè)物品,消費(fèi)者消費(fèi)10個(gè)物品
void producer(Queue* que)//生產(chǎn)者線程
{
	for (int i = 1; i <= 10; ++i)
	{
		que->put(i);
		std::this_thread::sleep_for(std::chrono::milliseconds(100));//睡眠100毫秒 
	}
}
void consumer(Queue* que)//消費(fèi)者線程
{
	for (int i = 1; i <= 10; ++i)
	{
		que->get();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));//睡眠100毫秒 
	}
}
int main()
{
	Queue que;	//兩個(gè)線程共享的隊(duì)列 
	std::thread t1(producer, &que);//開啟生產(chǎn)者線程 
	std::thread t2(consumer, &que);//開啟消費(fèi)者線程 
	//主線程等待兩個(gè)子線程都執(zhí)行完再結(jié)束。
	t1.join(); 
	t2.join();
	return 0;
}

在這里插入圖片描述

3、lock_gard和unique_lock

lock_gard和unique_lock可以看成unique_ptr和scope_ptr之間的關(guān)系,lock_gard和unique_lock做的事情是一樣的,都是在構(gòu)造函數(shù)中國(guó)自動(dòng)執(zhí)行mutex的lock()函數(shù),在析構(gòu)函數(shù)中自動(dòng)執(zhí)行mutex的unlock()函數(shù)。

lock_gard源碼:

在這里插入圖片描述

unique_lock源碼:

在這里插入圖片描述

在這里插入圖片描述

4、流程分析

在這里插入圖片描述

  • 首先消費(fèi)者線程拿到互斥鎖,生產(chǎn)者線程沒有拿到互斥鎖,生產(chǎn)者線程處于阻塞狀態(tài);
  • 消費(fèi)者線程發(fā)現(xiàn)que隊(duì)列是空的,進(jìn)入while循環(huán),進(jìn)入的等待狀態(tài),將互斥鎖釋放(都是由條件變量控制的);
  • 此時(shí)生產(chǎn)者線程拿到互斥鎖,不進(jìn)入while循環(huán),生產(chǎn)對(duì)象放入que隊(duì)列中,notify_all通知其他線程,也就是消費(fèi)者線程,由等待狀態(tài)進(jìn)入阻塞狀態(tài), 當(dāng)生產(chǎn)者線程函數(shù)完之后,出了函數(shù)作用域,將mutex互斥鎖釋放了,消費(fèi)者線程拿到鎖了,阻塞狀態(tài)變?yōu)檫\(yùn)行狀態(tài),繼續(xù)向下執(zhí)行;
  • 消費(fèi)者線程消費(fèi)完之后,繼續(xù)通知生產(chǎn)者,我消費(fèi)完了。

到此這篇關(guān)于C++詳細(xì)分析線程間的同步通信的文章就介紹到這了,更多相關(guān)C++線程同步通信內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論