最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解C++11中的線程庫(kù)

 更新時(shí)間:2022年01月27日 15:04:52   作者:精致的灰(>_<)  
線程是操作系統(tǒng)中的一個(gè)概念,線程對(duì)象可以關(guān)聯(lián)一個(gè)線程,用來(lái)控制線程以及獲取線程的狀態(tài),這篇文章主要介紹了C++11中的線程庫(kù)的相關(guān)知識(shí),需要的朋友可以參考下

一、線程庫(kù)的介紹

在C++11之前,涉及到多線程問(wèn)題,都是和平臺(tái)相關(guān)的,比如windows和linux下各有自己的接口,這使得代碼的可移植性比較差。C++11中最重要的特性就是對(duì)線程進(jìn)行支持了,使得C++在并行編程時(shí)不需要依賴第三方庫(kù),而且在原子操作中還引入了原子類的概念。要使用標(biāo)準(zhǔn)庫(kù)中的線程,必須包含< thread >頭文件。

特點(diǎn):跨平臺(tái)、面向?qū)ο蠓庋b的類(每個(gè)線程是個(gè)類對(duì)象)。

其實(shí)現(xiàn)原理是封裝庫(kù)時(shí)使用了條件編譯,也就是說(shuō)他的底層還是分別調(diào)用了不同平臺(tái)的線程API。

函數(shù)名功能
thread()構(gòu)造一個(gè)線程對(duì)象,沒(méi)有關(guān)聯(lián)任何線程函數(shù),即沒(méi)有啟動(dòng)任何線程
thread(fn,args1, args2,…)構(gòu)造一個(gè)線程對(duì)象,并關(guān)聯(lián)線程函數(shù)fn,args1,args2,…為線程函數(shù)的參數(shù)
get_id()獲取線程id
jionable()線程是否還在執(zhí)行,joinable代表的是一個(gè)正在執(zhí)行中的線程。
jion()該函數(shù)調(diào)用后會(huì)阻塞住線程,當(dāng)該線程結(jié)束后,主線程繼續(xù)執(zhí)行
detach()在創(chuàng)建線程對(duì)象后馬上調(diào)用,用于把被創(chuàng)建線程與線程對(duì)象分離開(kāi),分離的線程變?yōu)楹笈_(tái)線程,創(chuàng)建的線程的"死活"就與主線程無(wú)關(guān)

1.1. 使用時(shí)的注意點(diǎn)

線程是操作系統(tǒng)中的一個(gè)概念,線程對(duì)象可以關(guān)聯(lián)一個(gè)線程,用來(lái)控制線程以及獲取線程的狀態(tài)

當(dāng)創(chuàng)建一個(gè)線程對(duì)象后,沒(méi)有提供線程函數(shù),該對(duì)象實(shí)際沒(méi)有對(duì)應(yīng)任何線程。

#include <thread>
int main()
{
 std::thread t1;
 cout << t1.get_id() << endl;//0
 return 0; }

get_id()的返回值類型為id類型,id類型實(shí)際為std::thread命名空間下封裝的一個(gè)類,該類中包含了一個(gè)結(jié)構(gòu)體:

// vs下查看
typedef struct
{ /* thread identifier for Win32 */
 void *_Hnd; /* Win32 HANDLE */
 unsigned int _Id;
} _Thrd_imp_t;

當(dāng)創(chuàng)建一個(gè)線程對(duì)象后,并且給線程關(guān)聯(lián)線程函數(shù),該線程就被啟動(dòng),與主線程一起運(yùn)行。線程函數(shù)一般情況下可按照以下三種方式提供:函數(shù)指針、lambda表達(dá)式、函數(shù)對(duì)象

#include <iostream>
using namespace std;
#include <thread>
void ThreadFunc(int a) {
 cout << "Thread1" << a << endl; }
class TF
{
public:
 void operator()()
 {
 cout << "Thread3" << endl;
 }
};
int main()
 // 線程函數(shù)為函數(shù)指針
 thread t1(ThreadFunc, 10);
 
 // 線程函數(shù)為lambda表達(dá)式
 thread t2([]{cout << "Thread2" << endl; });
 // 線程函數(shù)為函數(shù)對(duì)象
 TF tf;
 thread t3(tf);
 t1.join();
 t2.join();
 t3.join();
 cout << "Main thread!" << endl;
 return 0; }

4.thread類是防拷貝的,不允許拷貝構(gòu)造以及賦值,但是可以移動(dòng)構(gòu)造和移動(dòng)賦值,即將一個(gè)線程對(duì)象關(guān)聯(lián)線程的狀態(tài)轉(zhuǎn)移給其他線程對(duì)象,轉(zhuǎn)移期間不意向線程的執(zhí)行

5.可以通過(guò)jionable()函數(shù)判斷線程是否是有效的,如果是以下任意情況,則線程無(wú)效:

  • 采用無(wú)參構(gòu)造函數(shù)構(gòu)造的線程對(duì)象(沒(méi)有提供線程函數(shù),即這個(gè)線程本身就是無(wú)效的)
  • 線程對(duì)象的狀態(tài)已經(jīng)轉(zhuǎn)移給其他線程對(duì)象(資源被轉(zhuǎn)移)
  • 線程已經(jīng)調(diào)用jion或者detach結(jié)束(線程已結(jié)束,或者已經(jīng)分離)

1.2. 線程函數(shù)參數(shù)

線程函數(shù)的參數(shù)是以值拷貝的方式拷貝到線程??臻g中的,因此:即使線程參數(shù)為引用類型,在線程中修改后也不能修改外部實(shí)參,因?yàn)槠鋵?shí)際引用的是線程棧中的拷貝,而不是外部實(shí)參。

注意:如果是類成員函數(shù)作為線程參數(shù)時(shí),必須將this作為線程函數(shù)參數(shù)

1.3. join與detach

啟動(dòng)了一個(gè)線程后,當(dāng)這個(gè)線程結(jié)束的時(shí)候,如何去回收線程所使用的資源呢?thread庫(kù)給我們兩種選擇:

join()方式
join():主線程被阻塞,當(dāng)新線程終止時(shí),join()會(huì)清理相關(guān)的線程資源,然后返回,主線程再繼續(xù)向下執(zhí)行,然后銷毀線程對(duì)象。由于join()清理了線程的相關(guān)資源,thread對(duì)象與已銷毀的線程就沒(méi)有關(guān)系了,因此一個(gè)線程對(duì)象只能使用一次join(),否則程序會(huì)崩潰。
采用jion()方式結(jié)束線程時(shí),jion()的調(diào)用位置非常關(guān)鍵。為了避免該問(wèn)題,可以采用RAII的方式對(duì)線程對(duì)象進(jìn)行封裝,RAII :利用對(duì)象聲明周期來(lái)管理線程資源,保證線程資源正常銷毀,資源獲取立即初始化,在構(gòu)造函數(shù)中初始化資源,在析構(gòu)函數(shù)中銷毀資源。 比如:

#include <thread>
class mythread
{
public:
 explicit mythread(std::thread &t) :m_t(t){}
 ~mythread()
 {
 if (m_t.joinable())//判斷線程是否還在
 m_t.join();
 }
 mythread(mythread const&)=delete;
 mythread& operator=(const mythread &)=delete;
private:
 std::thread &m_t;
};
void ThreadFunc() { cout << "ThreadFunc()" << endl; }
bool DoSomething() { return false; }
int main()
{
 thread t(ThreadFunc);
 mythread q(t);
 if (DoSomething())
 return -1;
 return 0; 
}

detach()方式
detach():該函數(shù)被調(diào)用后,新線程與線程對(duì)象分離,不再被線程對(duì)象所表達(dá),就不能通過(guò)線程對(duì)象控制線程了,新線程會(huì)在后臺(tái)運(yùn)行,其所有權(quán)和控制權(quán)將會(huì)交給c++運(yùn)行庫(kù)。同時(shí),C++運(yùn)行庫(kù)保證,當(dāng)線程退出時(shí),其相關(guān)資源的能夠正確的回收。

注意:線程對(duì)象銷毀前,要么以jion()的方式等待線程結(jié)束,要么以detach()的方式將線程與線程對(duì)象分離。

二、原子性操作庫(kù)

多線程最主要的問(wèn)題是共享數(shù)據(jù)帶來(lái)的問(wèn)題(即線程安全)。如果共享數(shù)據(jù)都是只讀的,那么沒(méi)問(wèn)題,因?yàn)?strong>只讀操作不會(huì)影響到數(shù)據(jù),更不會(huì)涉及對(duì)數(shù)據(jù)的修改,所以所有線程都會(huì)獲得同樣的數(shù)據(jù)。但是,當(dāng)一個(gè)或多個(gè)線程要修改共享數(shù)據(jù)時(shí),就會(huì)產(chǎn)生很多潛在的麻煩。

2.1. atomic

雖然加鎖可以解決,但是加鎖有一個(gè)缺陷就是:只要一個(gè)線程在對(duì)sum++時(shí),其他線程就會(huì)被阻塞,會(huì)影響程序運(yùn)行的效率,而且鎖如果控制不好,還容易造成死鎖。
因此C++11中引入了原子操作。所謂原子操作:即不可被中斷的一個(gè)或一系列操作,C++11引入的原子操作類型,使得線程間數(shù)據(jù)的同步變得非常高效。

注意:原子類型通常屬于"資源型"數(shù)據(jù),多個(gè)線程只能訪問(wèn)單個(gè)原子類型的拷貝,因此在C++11中,原子類型只能從其模板參數(shù)中進(jìn)行構(gòu)造,不允許原子類型進(jìn)行拷貝構(gòu)造、移動(dòng)構(gòu)造以及operator=等,為了防止意外,標(biāo)準(zhǔn)庫(kù)已經(jīng)將atmoic模板類中的拷貝構(gòu)造、移動(dòng)構(gòu)造、賦值運(yùn)算符重載默認(rèn)刪除掉了。

#include<thread>
#include<atomic>
atomic<int>x = 0;
void Add(int n)
{

	for (int i = 0; i < n; i++)
	{
		x++;
	}
}
int main()
	thread t1(Add, 10000);
	thread t2(Add, 10000);
	t1.join();
	t2.join();
	cout << x << endl;//20000
	return 0;

2.2. 鎖

在多線程環(huán)境下,如果想要保證某個(gè)變量的安全性,只要將其設(shè)置成對(duì)應(yīng)的原子類型即可,即高效又不容易出現(xiàn)死鎖問(wèn)題。但是有些情況下,我們可能需要保證一段代碼的安全性,那么就只能通過(guò)鎖的方式來(lái)進(jìn)行控制。

1.lock、unlock:

線程函數(shù)調(diào)用lock()時(shí),可能會(huì)發(fā)生以下三種情況:

  • 如果該互斥量當(dāng)前沒(méi)有被鎖住,則調(diào)用線程將該互斥量鎖住,直到調(diào)用 unlock之前,該線程一直擁有該鎖。
  • 如果當(dāng)前互斥量被其他線程鎖住,則當(dāng)前的調(diào)用線程被阻塞住。
  • 如果當(dāng)前互斥量被當(dāng)前調(diào)用線程鎖住,則會(huì)產(chǎn)生死鎖(同一線程,不可以連續(xù)調(diào)用兩次鎖)。

線程函數(shù)調(diào)用try_lock()時(shí),可能會(huì)發(fā)生以下三種情況:

  • 如果當(dāng)前互斥量沒(méi)有被其他線程占有,則該線程鎖住互斥量,直到該線程調(diào)用 unlock 釋放互斥量。
  • 如果當(dāng)前互斥量被其他線程鎖住,則當(dāng)前調(diào)用線程返回 false,而并不會(huì)被阻塞掉(非阻塞式)。
  • 如果當(dāng)前互斥量被當(dāng)前調(diào)用線程鎖住,則會(huì)產(chǎn)生死鎖。

2.recursive_mutex:
允許同一個(gè)線程對(duì)互斥量多次上鎖(即遞歸上鎖),來(lái)獲得對(duì)互斥量對(duì)象的多層所有權(quán),釋放互斥量時(shí)需要調(diào)用與該鎖層次深度相同次數(shù)的 unlock(),除此之外,recursive_mutex 的特性和mutex 大致相同。

3.timed_mutex:
比 mutex 多了兩個(gè)成員函數(shù),try_lock_for(),try_lock_until()

try_lock_for()
接受一個(gè)時(shí)間范圍,表示在這一段時(shí)間范圍之內(nèi)線程如果沒(méi)有獲得鎖則被阻塞?。ㄅc std::mutex的 try_lock() 不同,try_lock 如果被調(diào)用時(shí)沒(méi)有獲得鎖則直接返回 false),如果在此期間其他線程釋放了鎖,則該線程可以獲得對(duì)互斥量的鎖,如果超時(shí)(即在指定時(shí)間內(nèi)還是沒(méi)有獲得鎖),則返回 false。

try_lock_until()
接受一個(gè)時(shí)間點(diǎn)作為參數(shù),在指定時(shí)間點(diǎn)未到來(lái)之前線程如果沒(méi)有獲得鎖則被阻塞住,如果在此期間其他線程釋放了鎖,則該線程可以獲得對(duì)互斥量的鎖,如果超時(shí)(即在指定時(shí)間內(nèi)還是沒(méi)有獲得鎖),則返回 false。

int sum = 0;

timed_mutex Lock;
chrono::milliseconds timeout(100);//100毫秒
void func(int number)
{
	for (int i = 0; i < number; i++)
	{
		
		//Lock.try_lock_for(timeout);//接收一個(gè)時(shí)間范圍
		Lock.try_lock_until(chrono::steady_clock::now() + timeout);//接收一個(gè)時(shí)間點(diǎn)
		sum++;
		Lock.unlock();
	}
}

4.recursive_timed_mutex
是recursive_mutex、timed_mutex兩種鎖的結(jié)合。

5.lock_guard(守衛(wèi)鎖):
加鎖和解鎖的過(guò)程,通過(guò)類的構(gòu)造和析構(gòu)來(lái)自動(dòng)控制加鎖和釋放鎖(RAII思想)

template<class _Mutex>
class lock_guard
{
public:

	// 構(gòu)造函數(shù)加鎖
	explicit lock_guard(_Mutex& _Mtx)
		: _MyMutex(_Mtx)
	{
		_MyMutex.lock();
	}
	//通過(guò)析構(gòu)函數(shù)釋放鎖
	~lock_guard() _NOEXCEPT
		_MyMutex.unlock();
	//防拷貝和防賦值
	lock_guard(const lock_guard&) = delete;
	lock_guard& operator=(const lock_guard&) = delete;
private:
	_Mutex& _MyMutex;
};
int sum = 0;
mutex Lock;
void func(int number)
{
	for (int i = 0; i < number; i++)
	{
		lock_guard<mutex> lg(Lock);
		sum++;
	}
}

lock_guard類模板主要是通過(guò)RAII的方式,對(duì)其管理的互斥量進(jìn)行了封裝,在需要
加鎖的地方,只需要用上述介紹的任意互斥體實(shí)例化一個(gè)lock_guard,調(diào)用構(gòu)造函數(shù)成功上鎖,出作用域前,lock_guard對(duì)象要被銷毀,調(diào)用析構(gòu)函數(shù)自動(dòng)解鎖,可以有效避免死鎖問(wèn)題。
lock_guard的缺陷:太單一,用戶沒(méi)有辦法對(duì)該鎖進(jìn)行控制,因此C++11又提供了unique_lock。

6.unique_lock

與lock_gard類似,unique_lock類模板也是采用RAII的方式對(duì)鎖進(jìn)行了封裝,并且也是以獨(dú)占所有權(quán)的方式管理mutex對(duì)象的上鎖和解鎖操作,即其對(duì)象之間不能發(fā)生拷貝。在構(gòu)造(或移動(dòng)(move)賦值)時(shí),unique_lock 對(duì)象需要傳遞一個(gè) Mutex 對(duì)象作為它的參數(shù),新創(chuàng)建的 unique_lock 對(duì)象負(fù)責(zé)傳入的 Mutex對(duì)象的上鎖和解鎖操作。使用以上類型互斥量實(shí)例化unique_lock的對(duì)象時(shí),自動(dòng)調(diào)用構(gòu)造函數(shù)上鎖,unique_lock對(duì)象銷毀時(shí)自動(dòng)調(diào)用析構(gòu)函數(shù)解鎖,可以很方便的防止死鎖問(wèn)題。 與lock_guard不同的是,unique_lock更加的靈活,提供了更多的成員函數(shù):

  • 上鎖/解鎖操作:lock、try_lock、try_lock_for、try_lock_until和unlock
  • 修改操作:移動(dòng)賦值、交換(swap:與另一個(gè)unique_lock對(duì)象互換所管理的互斥量所有權(quán))、釋放(release:返回它所管理的互斥量對(duì)象的指針,并釋放所有權(quán))
  • 獲取屬性:owns_lock(返回當(dāng)前對(duì)象是否上了鎖)、operator bool()(與owns_lock()的功能相同)、mutex(返回當(dāng)前unique_lock所管理的互斥量的指針)。

三、使用lambda表達(dá)式創(chuàng)建多個(gè)線程

#include<iostream>
#include<thread>
#include<atomic>
#include<vector>
using namespace std;
int main()
{

	atomic<int>x = 0;
	//m個(gè)線程對(duì)x加n次
	int m, n;
	cin >> m >> n;
	vector<thread>vthreads;
	for (int i = 0; i < m; i++)
	{
		vthreads.push_back(thread([&x](int count) {
			for (int i = 0; i < count; i++)
			{
				x++;
			}
			}, n));
	}
	for (auto& t : vthreads)
		cout << t.get_id() <<".join()" << endl;
		t.join();
	cout << x << endl;
	return 0;
}

四、條件變量

需要注意的是,傳入wait的鎖不能是lock_guard,這是因?yàn)閘ock_guard沒(méi)有解鎖接口

喚醒:

Notify one -> 喚醒一個(gè)
notify_all -> 喚醒一批

#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
using namespace std;

int main()
{
	int n = 20;
	mutex mtx1,mtx2;
	unique_lock<mutex> t;
	condition_variable cv1,cv2;
	//使用兩個(gè)線程打印0-n的數(shù),一個(gè)打印奇數(shù),一個(gè)打印偶數(shù)
	thread t1([&]() 
		{
			for (int i = 0; i < n; i+=2)
			{
			
				cout << this_thread::get_id() << ":" << i << endl;
				cv2.notify_one();//打印偶數(shù)以后通知t2
				unique_lock<mutex> lock(mtx1);
				cv1.wait(lock);
			}
		});
	thread t2([&]() 
		{
			for (int i = 1; i < n; i+=2)
			{
				unique_lock<mutex> lock(mtx2);
				cv2.wait(lock);
				cout << this_thread::get_id() << ":" << i << endl;
				cv1.notify_one();//t2打印奇數(shù)以后,通知t1
			}
		});
	t1.join();
	t2.join();
	return 0;
}

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

相關(guān)文章

  • C++獲取MD5算法實(shí)現(xiàn)代碼

    C++獲取MD5算法實(shí)現(xiàn)代碼

    這篇文章主要介紹了C++獲取MD5算法實(shí)現(xiàn)代碼,這個(gè)是網(wǎng)上扒下來(lái)的 作者已經(jīng)無(wú)法知道是誰(shuí)了 ,可以備用
    2019-04-04
  • 利用C語(yǔ)言替換文件中某一行的方法

    利用C語(yǔ)言替換文件中某一行的方法

    大家都知道C語(yǔ)言提供了文件操作,但是替換文件的某一行比較麻煩,下面是我使用的一個(gè)方法,現(xiàn)在分享給大家,有需要的朋友們可以參考借鑒。
    2016-09-09
  • C++中棧結(jié)構(gòu)建立與操作詳細(xì)解析

    C++中棧結(jié)構(gòu)建立與操作詳細(xì)解析

    我們可以把棧理解成一個(gè)大倉(cāng)庫(kù),放在倉(cāng)庫(kù)門口(棧頂)的貨物會(huì)優(yōu)先被取出,然后再取出里面的貨物。而從數(shù)據(jù)的邏輯結(jié)構(gòu)來(lái)看,棧結(jié)構(gòu)起始就是一種線性結(jié)構(gòu)
    2013-10-10
  • C語(yǔ)言全面細(xì)致精講關(guān)鍵字的使用

    C語(yǔ)言全面細(xì)致精講關(guān)鍵字的使用

    關(guān)鍵字是C語(yǔ)言非常重要的一部分,熟練的掌握和使用關(guān)鍵字有助于我們更加熟悉了解C語(yǔ)言,同時(shí)C語(yǔ)言的關(guān)鍵字也是面試筆試中??嫉膬?nèi)容。C語(yǔ)言的關(guān)鍵字共有32個(gè),但并不是每個(gè)關(guān)鍵字都有坑,本篇文章將通過(guò)理論聯(lián)系實(shí)際的方式為大家講解C語(yǔ)言中易混易錯(cuò)以及常考的一些關(guān)鍵字
    2022-05-05
  • C++中Socket網(wǎng)絡(luò)編程實(shí)例詳解

    C++中Socket網(wǎng)絡(luò)編程實(shí)例詳解

    這篇文章主要介紹了C++中Socket網(wǎng)絡(luò)編程實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • 一篇文章帶你了解C++中的異常

    一篇文章帶你了解C++中的異常

    這篇文章主要為大家詳細(xì)介紹了C++中的異常,使用數(shù)據(jù)庫(kù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C語(yǔ)言實(shí)現(xiàn)日志備份守護(hù)進(jìn)程的示例詳解

    C語(yǔ)言實(shí)現(xiàn)日志備份守護(hù)進(jìn)程的示例詳解

    這篇文章主要為大家詳細(xì)介紹了如何使用C語(yǔ)言開(kāi)發(fā)一個(gè)簡(jiǎn)單的日志備份守護(hù)進(jìn)程,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-04-04
  • C++中vector容器的常用操作方法實(shí)例總結(jié)

    C++中vector容器的常用操作方法實(shí)例總結(jié)

    vector容器一般被用作創(chuàng)建動(dòng)態(tài)數(shù)組,動(dòng)態(tài)數(shù)組就像Python中的list結(jié)構(gòu)一樣,可以比普通數(shù)組擁有更豐富操作方法,下面就為大家整理了一些最常用的操作:
    2016-05-05
  • EasyC++單獨(dú)編譯

    EasyC++單獨(dú)編譯

    這篇文章主要介紹了EasyC++單獨(dú)編譯,在上一篇當(dāng)中,我們編寫(xiě)好了頭文件coordin.h,現(xiàn)在我們要完成它的實(shí)現(xiàn)。需要的小伙伴可以先學(xué)習(xí)上一篇內(nèi)容然后一起與小編一起進(jìn)入本篇內(nèi)容一起學(xué)習(xí)吧
    2021-12-12
  • VC使用編譯時(shí)間作為版本號(hào)標(biāo)識(shí)的方法

    VC使用編譯時(shí)間作為版本號(hào)標(biāo)識(shí)的方法

    這篇文章主要介紹了VC使用編譯時(shí)間作為版本號(hào)標(biāo)識(shí)的方法,需要的朋友可以參考下
    2017-03-03

最新評(píng)論

朝阳县| 镇宁| 桃源县| 天长市| 镶黄旗| 宜君县| 南涧| 六枝特区| 东宁县| 商城县| 福建省| 洛浦县| 东平县| 赤峰市| 西乡县| 文山县| 鄂伦春自治旗| 新乡市| 平定县| 怀仁县| 上思县| 盐亭县| 准格尔旗| 南通市| 石泉县| 尖扎县| 达孜县| 内黄县| 寻乌县| 玉树县| 大同县| 根河市| 化德县| 抚松县| 霍林郭勒市| 航空| 延津县| 和平县| 陆良县| 沙坪坝区| 略阳县|