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

C++多線程編程超詳解

 更新時間:2021年09月29日 14:59:25   作者:UExplorer  
本文給大家介紹的是C++多線程編程,由于C++本身沒有多線程機制,在windows下我們使用調(diào)用SDK win32 api來實現(xiàn),示例都很簡單,講解的也很詳細,推薦給大家

C++多線程

1. 概念

  • 進程:一個在內(nèi)存中運行的應用程序。每個進程都有自己獨立的一塊內(nèi)存空間,一個進程可以有多個線程,比如在Windows系統(tǒng)中,一個運行的xx.exe就是一個進程。
  • 線程:進程中的一個執(zhí)行任務(控制單元),負責當前進程中程序的執(zhí)行。一個進程至少有一個線程,一個進程可以運行多個線程,多個線程可共享數(shù)據(jù)。與進程不同的是同類的多個線程共享進程的堆和方法區(qū)資源,但每個線程有自己的程序計數(shù)器、虛擬機棧和本地方法棧,所以系統(tǒng)在產(chǎn)生一個線程,或是在各個線程之間作切換工作時,負擔要比進程小得多,也正因為如此,線程也被稱為輕量級進程。
  • 并發(fā):并發(fā)指的是兩個或多個獨立的活動在同一時段內(nèi)發(fā)生。并發(fā)在生活中隨處可見:比如在跑步的時候同時聽音樂,在看電腦顯示器的同時敲擊鍵盤等。同一時間段內(nèi)可以交替處理多個操作,強調(diào)同一時段內(nèi)交替發(fā)生。
  • 并行:同一時刻內(nèi)同時處理多個操作,強調(diào)同一時刻點同時發(fā)生。

2. 常用API

​ 頭文件#include<thread>

1.thread

API 描述 注意
thread.join() 加入線程(會阻塞主線程,模擬同步操作)
thread.detach() 加入線程(不會阻塞主線程,模擬異步操作)
thread.joinable() 是否可加入線程,返回bool
thread.get_id() 獲取線程的ID
thread.hardware_concurrency() 獲取硬件并發(fā)的數(shù)量
thread.swap() 交換線程
thread.native_handle() 獲取原生handle,為windows多線程中CreateThread的返回值,使用這個handle從而可以實現(xiàn)線程的掛起喚醒

測試代碼:

void threadFunc01() {
	cout << "thread join1" << endl;
	this_thread::sleep_for(chrono::seconds(2));
}
void threadFunc02() {
	cout << "thread join2" << endl;
	this_thread::sleep_for(chrono::seconds(2));
}
void test01() {
	// 創(chuàng)建線程
	std::thread thread1(threadFunc01);
	std::thread thread2(threadFunc02);
	//thread.join(); //join 會阻塞主線程 同步操作
	//thread.detach(); //detach 不會阻塞主線程 異步操作
	bool bJoinAble = thread1.joinable();
	thread::id threadId = thread1.get_id();
	//hardware_concurrency 硬件并發(fā)的數(shù)量
	int threadNum = thread1.hardware_concurrency();
	cout << "hardware_concurrency:" << threadNum << endl;
	//應用 線程的預分配。
	for (int i = 0; i < thread1.hardware_concurrency(); i++) {
		std::thread threadRef(threadFunc01);
		threadRef.detach();
	}
	thread1.swap(thread2);
	thread1.join();
}

向線程里傳遞參數(shù)的方法

// 向線程里傳遞參數(shù)的方法
#include<string>
void threadFunc03(int num, const string& str) {
	cout << "num = " << num << " str = " << str << endl;
}
struct FObject {
	void Run(const string& str) {
		cout << str << endl;
	}
};
void test02() {
    // 通過函數(shù)綁定
	thread newThread1(threadFunc03, 10, "Unreal");
	newThread1.detach();
	// 通過lambda綁定
	int a = 50;
	thread newThread2([&](int num,const string& str) {
		cout << "a = " << a << " num = " << num << " str = " << str << endl;
		}, 1, "Unreal");
	newThread2.detach();

	// 綁定對象
	FObject objectRef;
	thread newThread3(&FObject::Run, objectRef, "Unreal");
	newThread3.detach();
}

2.互斥鎖mutex

​ 頭文件#include<mutex>

API 描述 注意
mutex.lock() 上鎖
mutex.unlock() 解鎖
mutex.try_lock() 判斷可不可以加鎖,返回bool 可以用該方法建立非阻塞模式

測試代碼:

#include<mutex>
mutex lockRef;
void threadFunc04(int num,const string& str) {
	// 進入該線程鎖住該線程,其他線程想要進入該線程需要排隊
	lockRef.lock();
	cout << "thread join4" << endl;
	this_thread::sleep_for(chrono::seconds(2));
	// 解鎖
	lockRef.unlock();
}
void test03() {
	std::thread thread1(threadFunc04, 10, "Unreal");
	std::thread thread2(threadFunc04, 5, "Unity");
	std::thread thread3(threadFunc04, 20, "Cocos");
	thread1.detach();
	thread2.detach();
	thread3.detach();
}

使用類加鎖的方式:

#include<mutex>
mutex lockRef;
struct FEvent {
	FEvent() {
		m.lock();
	}
	~FEvent()
	{
		m.unlock();
	}
	static mutex m;
};
mutex FEvent::m;
#define LOCK_SCOPE FEvent Event
void threadFunc04(int num,const string& str) {
	LOCK_SCOPE; //加上鎖,并且過了這個作用域自動解鎖(析構)
	cout << "thread join4" << endl;
	this_thread::sleep_for(chrono::seconds(2));
}
void test03() {
	std::thread thread1(threadFunc04, 10, "Unreal");
	std::thread thread2(threadFunc04, 5, "Unity");
	std::thread thread3(threadFunc04, 20, "Cocos");
	thread1.detach();
	thread2.detach();
	thread3.detach();
}

try_lock()

void threadFunc04(int num,const string& str) {
	bool bLock = FEvent::m.try_lock();
	if (bLock) {
		LOCK_SCOPE; //加上鎖,并且過了這個作用域自動解鎖(析構)
		cout << "thread join4" << endl;
		this_thread::sleep_for(chrono::seconds(2));
	}
}

​ 使用try_lock()可以進行判斷能不能上鎖,不能上鎖的話,就不用執(zhí)行上鎖后的代碼,防止其他線程阻塞在該線程。

lock_guard

lock_guard是一種鎖類,作用和我們上面自定義的鎖類FEvent相同,創(chuàng)建的時候鎖住目標線程,釋放的時候解鎖。

// 聲明方式
lock_guard<mutex>ref;

源碼:

template <class _Mutex>
class lock_guard { // class with destructor that unlocks a mutex
public:
    using mutex_type = _Mutex;
    explicit lock_guard(_Mutex& _Mtx) : _MyMutex(_Mtx) { // construct and lock
        _MyMutex.lock();
    }
    lock_guard(_Mutex& _Mtx, adopt_lock_t) : _MyMutex(_Mtx) { // construct but don't lock
    }
    ~lock_guard() noexcept {
        _MyMutex.unlock();
    }
    lock_guard(const lock_guard&) = delete;
    lock_guard& operator=(const lock_guard&) = delete;
private:
    _Mutex& _MyMutex;
};

unique_lock

​ 作用和lock_guard相同,唯一的不同之處,lock_guard開放的API只有析構函數(shù),而unique_lock開放的API非常多,即自由度比lock_guard高,可以定義鎖的行為。

void test05() {
	// defer_lock 關鍵字為延遲鎖,即創(chuàng)建該對象時不會鎖住該線程,什么時候鎖需要自定義
	std::unique_lock<mutex>lockRef2(FEvent::m,defer_lock);
	std::unique_lock<mutex>lockRef2(FEvent::m,chrono::seconds(2)); //鎖兩秒
	//....執(zhí)行
	lockRef2.lock();
	lockRef2.unlock();
	bool bLock1 = lockRef2.try_lock();//嘗試上鎖
	lockRef2.try_lock_for(chrono::seconds(2)); //鎖2s
    mutex *lockRef3 = lockRef2.release(); //釋放鎖,同時會返回被釋放的這個鎖的指針對象
    bool bLock2 = lockRef2.owns_lock(); //當前是否被鎖住 
}

應用:

void test05() {
	//std::lock_guard<mutex>lockRef1(FEvent::m);
	// defer_lock 關鍵字為延遲鎖
	std::unique_lock<mutex>lockRef2(FEvent::m,defer_lock);
	lockRef2.lock();
	lockRef2.mutex();
	bool bLock = lockRef2.owns_lock();
	std::unique_lock<mutex>lockRef3;
	lockRef2.swap(lockRef3);
	std::unique_lock<mutex>lockRef4 = move(lockRef3);
	lockRef4.unlock();
}

3. 掛起和喚醒

​ 頭文件#include<windows.h>

1111111

111111

111111

11111

111111

測試代碼:

#include<windows.h>
void threadFunc05() {
	while (true)
	{
		Sleep(10);
		cout << "threadFunc05" << endl;
	}
}

void test04() {
	thread thread1(threadFunc05);
	// 掛起線程
	SuspendThread(thread1.native_handle());
	Sleep(2);
	// 喚醒線程
	ResumeThread(thread1.native_handle());
}

如何高效將主線程資源進行轉移:

void threadFunc06(const char* str) {
	cout << str << endl;
}
void test04() {
	// 如何高效轉移線程資源
	// 使用std::move
	thread thread2(threadFunc06, move("Unreal")); // 使用move避免了拷貝
	thread thread3 = move(thread2);
	thread3.detach();
}

3. 應用場景

3.1 call_once執(zhí)行一次的函數(shù)

​ 通過使用該函數(shù),用來防止多線程的多次觸發(fā)。

once_flag tag;
void callonceTest() {
	call_once(tag, [&]() {
		cout << "Do once" << endl;
		});
}
void test06() {
	for (int i = 0; i < 10; i++) {
		thread thread1(callonceTest);
		thread1.detach();
	}
}

3.2 condition_variable條件鎖

​ 使用需要包含頭文件#include<condition_variable>

可以使用條件鎖來達到同步的作用,即當滿足一定的條件后才解鎖某個線程。

#include<condition_variable>
condition_variable condition_lock;
mutex mutexLock;
void conditionFuncTest() {
	unique_lock<mutex>lock(mutexLock);
	condition_lock.wait(lock);  //鎖住該線程
	cout << "Run" << endl;
}
void test12() {
	std::thread threadRef(conditionFuncTest);
	threadRef.detach();
	Sleep(3000); //3s后再激活
	condition_lock.notify_one();
}

3.3 future獲取線程的計算結果

​ 通過使用future可以得到"未來"線程被調(diào)用的時候計算得返回值,使用時需要包含頭文件#include<future>。

聲明方式:

// async為創(chuàng)建該線程的方式為異步 funName 函數(shù)名 args為傳入的函數(shù)參數(shù)
std::future<string>newFuture = std::async(launch::async, funName,args...);

應用:

#include<future> 
string getString(int num) {
	return "Unreal";
}
void test08() {
	std::future<string>newFuture = std::async(launch::async, getString, 10);
	//std::future<string>newFuture = std::async(launch::deferred, getString, 10); // 睡一秒再執(zhí)行
	Sleep(1000);
	string str = newFuture.get(); //get只能調(diào)用一次 調(diào)第二次會崩潰
	// 防止崩潰的寫法
	if (newFuture.valid()) {
		string str = newFuture.get();
	}
}

3.4 promise主線程如何將數(shù)據(jù)發(fā)送數(shù)據(jù)到其他線程

​ 通過使用promise(承諾)來進行進程之間的交互,常配合std::future使用。其作用是在一個線程t1中保存一個類型typename T的值,可供相綁定的std::future對象在另一線程t2中獲取。

​ 測試代碼:

// promise
string promiseTest(future<string>& future) {
	cout << future.get() << endl;
	return "Unreal";
}
void test09() {
	promise<string> promiseRef;
	future<string>future1 = promiseRef.get_future();
	future<string>future2 = std::async(launch::async, promiseTest, std::ref(future1)); //future 不支持值拷貝 需要傳遞引用
	promiseRef.set_value("Unreal is the best game engine in the world");
}

​ 但這里也有一個問題需要思考,如果需要發(fā)送數(shù)據(jù)到多個線程,是不是需要一個個的創(chuàng)建上面的代碼呢。這里就引出了多線程之間共享狀態(tài)這個解決方法。

3.5 future.share()多線程之間共享狀態(tài)

​ 通過future.share()我們可以很方便的使多個線程之間共享狀態(tài)。

現(xiàn)在來看看沒有使用該函數(shù)的話我們要共享狀態(tài)的話需要這么寫:

string promiseTest(future<string>& future) {
	cout << future.get() << endl;
	return "Unreal";
}
void test09() {
	promise<string> promiseRef;
	future<string>future1 = promiseRef.get_future();
	future<string>future2 = promiseRef.get_future();
	future<string>future3 = promiseRef.get_future();
	future<string>future4 = std::async(launch::async, promiseTest, std::ref(future1)); //future 不支持值拷貝 需要傳遞引用
	future<string>future5 = std::async(launch::async, promiseTest, std::ref(future2)); //future 不支持值拷貝 需要傳遞引用
	future<string>future6 = std::async(launch::async, promiseTest, std::ref(future3)); //future 不支持值拷貝 需要傳遞引用
	promiseRef.set_value("Unreal is the best game engine in the world");
}

使用了future.share()函數(shù)后:

string promiseTest02(shared_future<string> future) {
	cout << future.get() << endl;
	return "Unreal";
}
void test09() {
	promise<string> promiseRef;
	future<string>future1 = promiseRef.get_future();
    // shared_future
	shared_future<string> sharedFutrue1 = future1.share();
	future<string>future2 = std::async(launch::async, promiseTest02, sharedFutrue1); //shared_future 可以用拷貝傳遞
	future<string>future3 = std::async(launch::async, promiseTest02, sharedFutrue1);
	future<string>future4 = std::async(launch::async, promiseTest02, sharedFutrue1);
	promiseRef.set_value("Unreal is the best game engine in the world");
}

3.6 線程packaged_task

​ packaged_taskpromise非常相似,packaged_task<F>是對promise<T= std::function<F>>中T= std::function<F>這一可調(diào)對象(如函數(shù)、lambda表達式等)進行了包裝,簡化了使用方法。并將這一可調(diào)對象的返回結果傳遞給關聯(lián)的future對象。

綁定Lambda

void test10() {
	//綁定lambda
	packaged_task<int(int, int)> task1([](int a,int b) ->int{
		return a + b;
		});
	task1(1, 4);
	this_thread::sleep_for(chrono::seconds(1));
	if (task1.valid()) {
		auto f1 = task1.get_future();
		cout << f1.get() << endl;
	}
}

綁定普通函數(shù)

int packagedTest(int a,int b) {
	return a + b;
}
void test10() {
	//綁定函數(shù)
	packaged_task<int(int, int)>task2(packagedTest);
	task2(10, 5);
	this_thread::sleep_for(chrono::seconds(1));
	if (task2.valid()) {
		auto f2 = task2.get_future();
		cout << f2.get() << endl;
	}
}

使用std::bind進行函數(shù)綁定

int packagedTest(int a,int b) {
	return a + b;
}
void test10() {
	// bind
	packaged_task<int(int, int)>task3(std::bind(packagedTest,1,2));
	task3(10, 5); //因為bind使用了占位符 所以這里傳入的10 5失效了
	this_thread::sleep_for(chrono::seconds(1));
	if (task3.valid()) {
		auto f3 = task3.get_future();
		cout << f3.get() << endl; //1+2
	}
}

3.7 時間約束

void test11() {
	//休眠2s
	this_thread::sleep_for(chrono::seconds(2));
	// 休眠現(xiàn)在的時間加上2s
	chrono::steady_clock::time_point timePos = chrono::steady_clock::now() + chrono::seconds(2);
	this_thread::sleep_until(timePos);
}

4. Windows多線程

​ 使用WindowsAPI進行多線程的編寫,需要包含頭文件

#include<windows.h>

4.1 Windows創(chuàng)建線程

​ 使用CreateThread()創(chuàng)建線程

DWORD WINAPI funcThread(LPVOID lpPram) {
    // DWORD 類型為unsigned long
    // LPVOID 類型為void
    cout << "Unreal!" << endl;
    Sleep(1000);
    return 0l;
}
void windowsThreadTest01() {
	HANDLE handleRef = CreateThread(nullptr,0, funcThread,nullptr,0,nullptr);
    Sleep(2000);
    CloseHandle(handleRef); //使用之后需要關閉handle
}

​ 其中傳入的參數(shù)為:

/*
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
CreateThread(
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,  和線程安全有關 一般為null
    _In_ SIZE_T dwStackSize,                            線程棧的大小
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,         被線程執(zhí)行的回調(diào)函數(shù)
    _In_opt_ __drv_aliasesMem LPVOID lpParameter,       傳入線程的參數(shù)
    _In_ DWORD dwCreationFlags,                         創(chuàng)建線程的標志   參數(shù)0 代表立即啟動該線程
    _Out_opt_ LPDWORD lpThreadId                        傳出的線程ID
);
*/

4.2 Windows互斥鎖

// windows互斥鎖
HANDLE hMutex = nullptr;
DWORD WINAPI funcThread02(LPVOID lpParam) {
    cout << "Unreal" << endl;
    WaitForSingleObject(hMutex, INFINITE);
    Sleep(5000);
    ReleaseMutex(hMutex);
    return 0l;
}

void windowsThreadTest02() {
    hMutex = CreateMutex(nullptr, false, L"Mutex");
    HANDLE handleRef1 = CreateThread(nullptr, 0, funcThread02, nullptr, 0, nullptr);
    HANDLE handleRef2 = CreateThread(nullptr, 0, funcThread02, nullptr, 0, nullptr);
    CloseHandle(handleRef1);
    CloseHandle(handleRef2);
}

傳入的參數(shù)為:

/*
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
CreateMutexW(
    _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,      和線程安全有關一般為null
    _In_ BOOL bInitialOwner,                               有沒有該鎖的控制權
    _In_opt_ LPCWSTR lpName                                鎖名字
    );
*/

4.3 Windows掛起和喚醒線程

​ 通過使用SuspendThread(HandleRef)和ResumeThread(HandleRef)來掛起和喚醒線程

// windows 掛起喚醒
DWORD WINAPI funcThread03(LPVOID lpParam) {
    while (true) {
        Sleep(500);
        cout << "IsRunning" << endl;
    }
    return 0l;
}

void windowsThreadTest03() {
    HANDLE hRef = CreateThread(nullptr, 0, funcThread03, nullptr, 0, nullptr);
    SuspendThread(hRef);
    Sleep(2000);
    ResumeThread(hRef);
    CloseHandle(hRef);
}

總結

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內(nèi)容!

相關文章

  • C++深入淺出講解希爾排序算法的實現(xiàn)

    C++深入淺出講解希爾排序算法的實現(xiàn)

    希爾排序是希爾(Donald Shell)于1959年提出的一種排序算法。希爾排序也是一種插入排序,它是簡單插入排序經(jīng)過改進之后的一個更高效的版本,也稱為縮小增量排序,同時該算法是沖破O(n2)的第一批算法之一。本文會以圖解的方式詳細介紹希爾排序的基本思想及其代碼實現(xiàn)
    2022-05-05
  • C語言中操作進程信號的相關函數(shù)使用詳解

    C語言中操作進程信號的相關函數(shù)使用詳解

    這篇文章主要介紹了C語言中操作進程信號的相關函數(shù)使用詳解,分別是signal()函數(shù)和kill()函數(shù)的用法,需要的朋友可以參考下
    2015-09-09
  • C++?Qt利用GPU加速計算的示例詳解

    C++?Qt利用GPU加速計算的示例詳解

    這篇文章主要為大家詳細介紹了在?C++?和?Qt?中如何利用GPU加速計算,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-03-03
  • C++ 超詳細快速掌握二叉搜索樹

    C++ 超詳細快速掌握二叉搜索樹

    從這篇博客開始,我就要和大家介紹有關二叉搜索樹的知識,它還衍生出了兩棵樹——AVL樹和紅黑樹,在后面兩篇博客我都會介紹。今天先從二叉搜索樹開始引入
    2022-03-03
  • C++算法之在無序數(shù)組中選擇第k小個數(shù)的實現(xiàn)方法

    C++算法之在無序數(shù)組中選擇第k小個數(shù)的實現(xiàn)方法

    這篇文章主要介紹了C++算法之在無序數(shù)組中選擇第k小個數(shù)的實現(xiàn)方法,涉及C++數(shù)組的遍歷、判斷、運算等相關操作技巧,需要的朋友可以參考下
    2017-03-03
  • C++ Eigen庫實現(xiàn)最小二乘擬合的示例代碼

    C++ Eigen庫實現(xiàn)最小二乘擬合的示例代碼

    Eigen 是一個線性算術的 C++ 模板庫,功能強大、快速、優(yōu)雅以及支持多平臺,本文主要為大家介紹了C++利用Eigen庫實現(xiàn)最小二乘擬合的示例代碼,希望對大家有所幫助
    2023-07-07
  • C++條件及循環(huán)語句的綜合運用實例

    C++條件及循環(huán)語句的綜合運用實例

    這篇文章主要介紹了C++條件及循環(huán)語句的綜合運用實例,能夠幫助C++初學者更好地掌握C++的邏輯語句用法,需要的朋友可以參考下
    2015-09-09
  • C++:構造函數(shù),析構函數(shù)詳解

    C++:構造函數(shù),析構函數(shù)詳解

    今天小編就為大家分享一篇關于C++構造函數(shù)和析構函數(shù)的文章,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2021-09-09
  • C++實現(xiàn)屏幕截圖

    C++實現(xiàn)屏幕截圖

    這篇文章主要為大家詳細介紹了C++實現(xiàn)屏幕截圖功能,截圖自動保存為png格式文件,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • C++運行時獲取類型信息的type_info類與bad_typeid異常

    C++運行時獲取類型信息的type_info類與bad_typeid異常

    這篇文章主要介紹了C++運行時獲取類型信息的type_info類與bad_typeid異常,是C++入門學習中的基礎知識,需要的朋友可以參考下
    2016-01-01

最新評論

卢龙县| 麻阳| 万宁市| 荆州市| 山西省| 盖州市| 盖州市| 邵武市| 林西县| 修文县| 肥西县| 敦煌市| 汨罗市| 定西市| 长沙县| 灵山县| 扶风县| 会同县| 长武县| 呼伦贝尔市| 满城县| 铜鼓县| 自贡市| 新沂市| 辉南县| 霸州市| 平乡县| 武功县| 邵阳县| 浦江县| 兴化市| 兴城市| 通道| 永州市| 天台县| 景宁| 龙陵县| 双流县| 藁城市| 岗巴县| 屯留县|