91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何理解C++多線程編程

發布時間:2021-09-28 10:45:08 來源:億速云 閱讀:155 作者:iii 欄目:開發技術

這篇文章主要講解了“如何理解C++多線程編程”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何理解C++多線程編程”吧!

目錄
  • C++多線程

  • 1. 概念

    • 1.1 概念

  • 2. 常用API

    • 1.thread

    • 2.互斥鎖mutex

    • 3. 掛起和喚醒

  • 3. 應用場景

    • 3.1 call_once執行一次的函數

    • 3.2 condition_variable條件鎖

    • 3.3 future獲取線程的計算結果

    • 3.4 promise主線程如何將數據發送數據到其他線程

    • 3.5 future.share()多線程之間共享狀態

    • 3.6 線程packaged_task

    • 3.7 時間約束

  • 4. Windows多線程

    • 4.1 Windows創建線程

    • 4.2 Windows互斥鎖

    • 4.3 Windows掛起和喚醒線程

  • 總結

C++多線程

1. 概念

1.1 概念

  • 進程:一個在內存中運行的應用程序。每個進程都有自己獨立的一塊內存空間,一個進程可以有多個線程,比如在Windows系統中,一個運行的xx.exe就是一個進程。

  • 線程:進程中的一個執行任務(控制單元),負責當前進程中程序的執行。一個進程至少有一個線程,一個進程可以運行多個線程,多個線程可共享數據。與進程不同的是同類的多個線程共享進程的堆和方法區資源,但每個線程有自己的程序計數器、虛擬機棧和本地方法棧,所以系統在產生一個線程,或是在各個線程之間作切換工作時,負擔要比進程小得多,也正因為如此,線程也被稱為輕量級進程。

  • 并發:并發指的是兩個或多個獨立的活動在同一時段內發生。并發在生活中隨處可見:比如在跑步的時候同時聽音樂,在看電腦顯示器的同時敲擊鍵盤等。同一時間段內可以交替處理多個操作,強調同一時段內交替發生。

  • 并行:同一時刻內同時處理多個操作,強調同一時刻點同時發生。

2. 常用API

頭文件#include<thread>

1.thread

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

測試代碼:

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() {
	// 創建線程
	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 硬件并發的數量
	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();
}

向線程里傳遞參數的方法:

// 向線程里傳遞參數的方法
#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() {
    // 通過函數綁定
	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()可以進行判斷能不能上鎖,不能上鎖的話,就不用執行上鎖后的代碼,防止其他線程阻塞在該線程。

lock_guard

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

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

void test05() {
	// defer_lock 關鍵字為延遲鎖,即創建該對象時不會鎖住該線程,什么時候鎖需要自定義
	std::unique_lock<mutex>lockRef2(FEvent::m,defer_lock);
	std::unique_lock<mutex>lockRef2(FEvent::m,chrono::seconds(2)); //鎖兩秒
	//....執行
	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>

API描述注意
SuspendThread(thread.native_hadle())掛起線程
ResumeThread(thread.native_hadle())喚醒線程
Sleep()睡眠

測試代碼:

#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執行一次的函數

通過使用該函數,用來防止多線程的多次觸發。

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可以得到"未來"線程被調用的時候計算得返回值,使用時需要包含頭文件#include<future>。

聲明方式:

// async為創建該線程的方式為異步 funName 函數名 args為傳入的函數參數
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); // 睡一秒再執行
	Sleep(1000);
	string str = newFuture.get(); //get只能調用一次 調第二次會崩潰
	// 防止崩潰的寫法
	if (newFuture.valid()) {
		string str = newFuture.get();
	}
}

3.4 promise主線程如何將數據發送數據到其他線程

通過使用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");
}

但這里也有一個問題需要思考,如果需要發送數據到多個線程,是不是需要一個個的創建上面的代碼呢。這里就引出了多線程之間共享狀態這個解決方法。

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

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

現在來看看沒有使用該函數的話我們要共享狀態的話需要這么寫:

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()函數后:

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>這一可調對象(如函數、lambda表達式等)進行了包裝,簡化了使用方法。并將這一可調對象的返回結果傳遞給關聯的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;
	}
}

綁定普通函數

int packagedTest(int a,int b) {
	return a + b;
}
void test10() {
	//綁定函數
	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進行函數綁定

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));
	// 休眠現在的時間加上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創建線程

使用CreateThread()創建線程

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
}

其中傳入的參數為:

/*
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
CreateThread(
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,  和線程安全有關 一般為null
    _In_ SIZE_T dwStackSize,                            線程棧的大小
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,         被線程執行的回調函數
    _In_opt_ __drv_aliasesMem LPVOID lpParameter,       傳入線程的參數
    _In_ DWORD dwCreationFlags,                         創建線程的標志   參數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);
}

傳入的參數為:

/*
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);
}

感謝各位的閱讀,以上就是“如何理解C++多線程編程”的內容了,經過本文的學習后,相信大家對如何理解C++多線程編程這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

c++
AI

调兵山市| 且末县| 德保县| 宜兴市| 元氏县| 宜阳县| 财经| 长泰县| 新龙县| 山西省| 扬州市| 浙江省| 修水县| 玉环县| 运城市| 乌拉特前旗| 扎赉特旗| 伊春市| 义乌市| 左云县| 班戈县| 邛崃市| 和顺县| 谢通门县| 赤壁市| 洞头县| 沙雅县| 灵璧县| 临沧市| 台湾省| 张家口市| 西昌市| 麻栗坡县| 建宁县| 芦溪县| 莱阳市| 威远县| 江口县| 石泉县| 泊头市| 土默特右旗|