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

C++多線程編程超詳解

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

C++多線程

1. 概念

  • 進(jìn)程:一個(gè)在內(nèi)存中運(yùn)行的應(yīng)用程序。每個(gè)進(jìn)程都有自己獨(dú)立的一塊內(nèi)存空間,一個(gè)進(jìn)程可以有多個(gè)線程,比如在Windows系統(tǒng)中,一個(gè)運(yùn)行的xx.exe就是一個(gè)進(jìn)程。
  • 線程:進(jìn)程中的一個(gè)執(zhí)行任務(wù)(控制單元),負(fù)責(zé)當(dāng)前進(jìn)程中程序的執(zhí)行。一個(gè)進(jìn)程至少有一個(gè)線程,一個(gè)進(jìn)程可以運(yùn)行多個(gè)線程,多個(gè)線程可共享數(shù)據(jù)。與進(jìn)程不同的是同類的多個(gè)線程共享進(jìn)程的堆和方法區(qū)資源,但每個(gè)線程有自己的程序計(jì)數(shù)器、虛擬機(jī)棧和本地方法棧,所以系統(tǒng)在產(chǎn)生一個(gè)線程,或是在各個(gè)線程之間作切換工作時(shí),負(fù)擔(dān)要比進(jìn)程小得多,也正因?yàn)槿绱?,線程也被稱為輕量級進(jìn)程。
  • 并發(fā):并發(fā)指的是兩個(gè)或多個(gè)獨(dú)立的活動在同一時(shí)段內(nèi)發(fā)生。并發(fā)在生活中隨處可見:比如在跑步的時(shí)候同時(shí)聽音樂,在看電腦顯示器的同時(shí)敲擊鍵盤等。同一時(shí)間段內(nèi)可以交替處理多個(gè)操作,強(qiáng)調(diào)同一時(shí)段內(nèi)交替發(fā)生。
  • 并行:同一時(shí)刻內(nèi)同時(shí)處理多個(gè)操作,強(qiáng)調(diào)同一時(shí)刻點(diǎn)同時(shí)發(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的返回值,使用這個(gè)handle從而可以實(shí)現(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;
	//應(yīng)用 線程的預(yù)分配。
	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) {
	// 進(jìn)入該線程鎖住該線程,其他線程想要進(jìn)入該線程需要排隊(duì)
	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; //加上鎖,并且過了這個(gè)作用域自動解鎖(析構(gòu))
	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; //加上鎖,并且過了這個(gè)作用域自動解鎖(析構(gòu))
		cout << "thread join4" << endl;
		this_thread::sleep_for(chrono::seconds(2));
	}
}

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

lock_guard

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

// 聲明方式
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只有析構(gòu)函數(shù),而unique_lock開放的API非常多,即自由度比lock_guard高,可以定義鎖的行為。

void test05() {
	// defer_lock 關(guān)鍵字為延遲鎖,即創(chuàng)建該對象時(shí)不會鎖住該線程,什么時(shí)候鎖需要自定義
	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(); //釋放鎖,同時(shí)會返回被釋放的這個(gè)鎖的指針對象
    bool bLock2 = lockRef2.owns_lock(); //當(dāng)前是否被鎖住 
}

應(yīng)用:

void test05() {
	//std::lock_guard<mutex>lockRef1(FEvent::m);
	// defer_lock 關(guān)鍵字為延遲鎖
	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());
}

如何高效將主線程資源進(jìn)行轉(zhuǎn)移:

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

3. 應(yīng)用場景

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>

可以使用條件鎖來達(dá)到同步的作用,即當(dāng)滿足一定的條件后才解鎖某個(gè)線程。

#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獲取線程的計(jì)算結(jié)果

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

聲明方式:

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

應(yīng)用:

#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(承諾)來進(jìn)行進(jìn)程之間的交互,常配合std::future使用。其作用是在一個(gè)線程t1中保存一個(gè)類型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");
}

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

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

​ 通過future.share()我們可以很方便的使多個(gè)線程之間共享狀態(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表達(dá)式等)進(jìn)行了包裝,簡化了使用方法。并將這一可調(diào)對象的返回結(jié)果傳遞給關(guān)聯(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進(jìn)行函數(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); //因?yàn)閎ind使用了占位符 所以這里傳入的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 時(shí)間約束

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

4. Windows多線程

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

#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); //使用之后需要關(guān)閉handle
}

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

/*
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
CreateThread(
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,  和線程安全有關(guān) 一般為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)建線程的標(biāo)志   參數(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,      和線程安全有關(guān)一般為null
    _In_ BOOL bInitialOwner,                               有沒有該鎖的控制權(quán)
    _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);
}

總結(jié)

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

相關(guān)文章

最新評論