亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

1、多線程編程兩個問題

1.1、線程間的互斥

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

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

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

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

1.2、線程間的同步通信

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

通信就是:

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

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

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

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

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

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

在這里插入圖片描述

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

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

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

消費者消費的比較快,queue為空時,就會出錯;(需要有線程間的通信機制)

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

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

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

在這里插入圖片描述

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

#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)一個物品,通知消費者消費一個;消費完了,消費者再通知生產(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)該通知消費者去消費,消費者消費完了,生產(chǎn)者再繼續(xù)生產(chǎn)
			//生產(chǎn)者線程進入#1等待狀態(tài),并且#2把mtx互斥鎖釋放掉
			cv.wait(lck);//傳入一個互斥鎖,當(dāng)前線程掛起,處于等待狀態(tài),并且釋放當(dāng)前鎖 lck.lock()  lck.unlock
		}
		que.push(val);
		/*
		notify_one:通知喚醒另外的一個線程的
		notify_all:通知喚醒其它所有線程的
		通知其它所有的線程,我生產(chǎn)了一個物品,你們趕緊消費吧
		其它線程得到該通知,就會從等待狀態(tài) =》 到阻塞狀態(tài) =》 但是要獲取互斥鎖才能繼續(xù)向下執(zhí)行
		*/
		cv.notify_all();
		cout << "生產(chǎn)者 生產(chǎn):" << val << "號物品" << endl;
	}
	int get()//消費物品
	{
		lock_guard<std::mutex> guard(mtx);//相當(dāng)于scoped_ptr
		unique_lock<std::mutex> lck(mtx);//相當(dāng)于unique_ptr 更安全 
		while (que.empty())
		{
			//消費者線程發(fā)現(xiàn)que是空的,通知生產(chǎn)者線程先生產(chǎn)物品
			//#1 掛起,進入等待狀態(tài) #2 把互斥鎖mutex釋放
			cv.wait(lck);
		}//如果其他線程執(zhí)行notify了,當(dāng)前線程就會從等待狀態(tài) =》到阻塞狀態(tài) =》但是要獲取互斥鎖才能繼續(xù)向下執(zhí)行 
		int val = que.front();
		que.pop();
		cv.notify_all();//通知其它線程我消費完了,趕緊生產(chǎn)吧
		cout << "消費者 消費:" << val << "號物品" << endl;
		return val;
	}
private:
	queue<int> que;
};
//這里模擬生產(chǎn)者生產(chǎn)10個物品,消費者消費10個物品
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)//消費者線程
{
	for (int i = 1; i <= 10; ++i)
	{
		que->get();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));//睡眠100毫秒 
	}
}
int main()
{
	Queue que;	//兩個線程共享的隊列 
	std::thread t1(producer, &que);//開啟生產(chǎn)者線程 
	std::thread t2(consumer, &que);//開啟消費者線程 
	//主線程等待兩個子線程都執(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ù)中國自動執(zhí)行mutex的lock()函數(shù),在析構(gòu)函數(shù)中自動執(zhí)行mutex的unlock()函數(shù)。

lock_gard源碼:

在這里插入圖片描述

unique_lock源碼:

在這里插入圖片描述

在這里插入圖片描述

4、流程分析

在這里插入圖片描述

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

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

相關(guān)文章

最新評論