c++ 標(biāo)準(zhǔn)庫多線程問題小結(jié)
C++ 多線程編程允許程序同時執(zhí)行多個任務(wù),從而提高性能和響應(yīng)能力。C++11 引入了 <thread> 庫,使得多線程編程更加方便。以下是一些基本概念和示例,幫助你理解如何在 C++ 中進(jìn)行多線程編程。
1. 創(chuàng)建線程
使用 std::thread 類可以創(chuàng)建一個新線程。你需要將一個函數(shù)或可調(diào)用對象傳遞給 std::thread 構(gòu)造函數(shù)。
#include <iostream>
#include <thread>
void threadFunction() {
std::cout << "Hello from thread!\\\\n";
}
int main() {
std::thread t(threadFunction); // 創(chuàng)建線程并執(zhí)行 threadFunction
t.join(); // 等待線程結(jié)束
std::cout << "Hello from main!\\\\n";
return 0;
}2. 傳遞參數(shù)給線程函數(shù)
你可以通過 std::thread 構(gòu)造函數(shù)傳遞參數(shù)給線程函數(shù)。
#include <iostream>
#include <thread>
void printMessage(const std::string& message) {
std::cout << message << "\\\\n";
}
int main() {
std::thread t(printMessage, "Hello from thread!");
t.join();
std::cout << "Hello from main!\\\\n";
return 0;
}3. 線程同步
多個線程可能會同時訪問共享資源,導(dǎo)致數(shù)據(jù)競爭。為了避免這種情況,可以使用互斥鎖(std::mutex)來保護(hù)共享資源。
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
std::mutex mtx; // 互斥鎖
void printNumber(int num) {
mtx.lock(); // 加鎖
std::cout << "Number: " << num << "\\\\n";
mtx.unlock(); // 解鎖
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back(printNumber, i);
}
for (auto& t : threads) {
t.join();
}
return 0;
}4. 使用 std::lock_guard 自動管理鎖
std::lock_guard 是一個 RAII 風(fēng)格的簡單的鎖管理器,它在構(gòu)造時自動加鎖,在析構(gòu)時自動解鎖。
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
std::mutex mtx;
void printNumber(int num) {
std::lock_guard<std::mutex> lock(mtx); // 自動加鎖和解鎖
std::cout << "Number: " << num << "\\\\n";
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back(printNumber, i);
}
for (auto& t : threads) {
t.join();
}
return 0;
}5. 條件變量
條件變量(std::condition_variable)用于線程間的同步,允許一個線程等待另一個線程滿足某些條件。
配合std::condition_variable::wait() 函數(shù)的第一個參數(shù)的必須是比lock_guard更靈活控制也更復(fù)雜重度的鎖:std::unique_lock。它可以RAII自動析構(gòu),也可以手動lock/unlock,中間有的代碼段就可以釋放鎖。手動把它unlock之后只是解鎖,沒有銷毀,后續(xù)可以按需復(fù)用再次 lock/unlock。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void printMessage() {
std::**unique_lock**<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 等待條件滿足
std::cout << "Hello from thread!\\\\n";
}
int main() {
std::thread t(printMessage);
{
std::lock_guard<std::mutex> lock(mtx);
ready = true; // 設(shè)置條件為 true
}
cv.notify_one(); // 通知等待的線程
t.join();
std::cout << "Hello from main!\\\\n";
return 0;
}
相比lock_guard的優(yōu)勢:
1. 靈活性:unique_lock 支持延遲鎖定(可以先構(gòu)造對象而不立即加鎖),而 lock_guard 在構(gòu)造時就必須加鎖。這意味著你可以先創(chuàng)建 unique_lock 對象,然后根據(jù)程序邏輯需要時再調(diào)用 lock() 或 unlock() 方法進(jìn)行手動加鎖或解鎖。
2. 條件變量的支持:unique_lock 可以與標(biāo)準(zhǔn)庫中的條件變量一起使用,如 std::condition_variable,這是 lock_guard 所不具備的功能。這是因?yàn)闂l件變量需要能夠原子地釋放鎖并進(jìn)入等待狀態(tài),這正是 unique_lock 提供的能力之一。
3. 鎖的所有權(quán)轉(zhuǎn)移:unique_lock 支持移動語義(move semantics),允許將鎖的所有權(quán)從一個 unique_lock 對象轉(zhuǎn)移到另一個對象,從而使得鎖可以在不同的作用域中傳遞。而 lock_guard 不支持這種操作,它的鎖所有權(quán)是固定的。
4. 嘗試鎖定(try-locking):除了基本的 lock() 和 unlock() 方法外,unique_lock 還提供了 try_lock() 方法,該方法嘗試獲取鎖但不會阻塞線程,如果無法獲得鎖則立即返回失敗結(jié)果。這對于避免線程長時間阻塞非常有用。
wait第二個參數(shù)predicate謂詞的用法參見:
<https://en.cppreference.com/w/cpp/thread/condition_variable/wait>
predicate不滿足不會結(jié)束等待執(zhí)行后續(xù)語句。6. 線程池
C++ 標(biāo)準(zhǔn)庫沒有直接提供線程池的實(shí)現(xiàn),但你可以使用第三方庫(如 Boost)或自己實(shí)現(xiàn)一個簡單的線程池。
#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
class ThreadPool {
public:
ThreadPool(size_t numThreads) {
for (size_t i = 0; i < numThreads; ++i) {
workers.emplace_back([this] {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(this->queueMutex);
this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });
if (this->stop && this->tasks.empty()) return;
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
});
}
}
template<class F>
void enqueue(F&& f) {
{
std::unique_lock<std::mutex> lock(queueMutex);
tasks.emplace(std::forward<F>(f));
}
condition.notify_one();
}
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queueMutex);
stop = true;
}
condition.notify_all();
for (std::thread &worker : workers) {
worker.join();
}
}
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queueMutex;
std::condition_variable condition;
bool stop = false;
};
int main() {
ThreadPool pool(4);
for (int i = 0; i < 8; ++i) {
pool.enqueue([i] {
std::cout << "Task " << i << " is running on thread " << std::this_thread::get_id() << "\\\\n";
});
}
return 0;
}7. 線程局部存儲
線程局部存儲(Thread Local Storage, TLS)允許每個線程擁有自己的變量實(shí)例。C++11 引入了 thread_local 關(guān)鍵字來實(shí)現(xiàn)這一點(diǎn)。
#include <iostream>
#include <thread>
thread_local int threadLocalVar = 0;
void threadFunction(int id) {
threadLocalVar = id;
std::cout << "Thread " << id << " has threadLocalVar = " << threadLocalVar << "\\\\n";
}
int main() {
std::thread t1(threadFunction, 1);
std::thread t2(threadFunction, 2);
t1.join();
t2.join();
return 0;
}8. 異步任務(wù)
C++11 還引入了 std::async 和 std::future,用于異步執(zhí)行任務(wù)并獲取結(jié)果。
#include <iostream>
#include <future>
int compute() {
std::this_thread::sleep_for(std::chrono::seconds(2));
return 42;
}
int main() {
// 啟動異步任務(wù)
std::future<int> fut = std::async(std::launch::async, compute);
// 獲取結(jié)果
int result = fut.get();
std::cout << "Result: " << result << std::endl;
return 0;
}
//使用 std::packaged_task (包在線程函數(shù)外)------------------------------------------
#include <iostream>
#include <future>
#include <thread>
int compute() {
std::this_thread::sleep_for(std::chrono::seconds(2));
return 42;
}
int main() {
// 創(chuàng)建 packaged_task
std::packaged_task<int()> task(compute);
// 獲取 future
std::future<int> fut = task.get_future();
// 在另一個線程中執(zhí)行任務(wù)
std::thread t(std::move(task));
t.join();
// 獲取結(jié)果
int result = fut.get();
std::cout << "Result: " << result << std::endl;
return 0;
}
// 使用 std::promise (作為線程函數(shù)參數(shù)) -----------------------------------------------
#include <iostream>
#include <future>
#include <thread>
void compute(std::promise<int> prom) {
std::this_thread::sleep_for(std::chrono::seconds(2));
prom.set_value(42);
}
int main() {
// 創(chuàng)建 promise 和 future
std::promise<int> prom;
std::future<int> fut = prom.get_future();
// 在另一個線程中執(zhí)行任務(wù)
std::thread t(compute, std::move(prom));
// 獲取結(jié)果
int result = fut.get();
std::cout << "Result: " << result << std::endl;
t.join();
return 0;
}異步機(jī)制總結(jié): C++11 的異步機(jī)制(std::future、std::async、std::packaged_task 和 std::promise)相比傳統(tǒng)的多線程編程,提供了以下額外的好處:
更高的抽象層次,簡化了異步操作的管理。
自動化的結(jié)果傳遞和異常處理。
更靈活的線程管理和任務(wù)執(zhí)行策略。
更清晰的代碼結(jié)構(gòu)和更低的耦合度。
支持任務(wù)組合和超時等待。
這些機(jī)制使異步編程更加直觀、安全和高效,是現(xiàn)代 C++ 并發(fā)編程的重要組成部分。
總結(jié)
C++ 多線程編程提供了強(qiáng)大的工具來處理并發(fā)任務(wù)。通過使用 std::thread、std::mutex、std::condition_variable 等工具,你可以編寫高效且安全的多線程程序。需要注意的是,多線程編程容易引入數(shù)據(jù)競爭和死鎖等問題,因此需要仔細(xì)設(shè)計和測試。
到此這篇關(guān)于c++ 標(biāo)準(zhǔn)庫多線程的文章就介紹到這了,更多相關(guān)c++ 標(biāo)準(zhǔn)庫多線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- c++標(biāo)準(zhǔn)庫讀寫ini文件的實(shí)現(xiàn)示例
- C++標(biāo)準(zhǔn)庫介紹及使用string類的詳細(xì)過程
- C++11標(biāo)準(zhǔn)庫 互斥鎖 <mutex> 詳解
- 詳解C++標(biāo)準(zhǔn)庫中處理正則表達(dá)式的類std::regex
- C++標(biāo)準(zhǔn)庫學(xué)習(xí)之weak_ptr智能指針用法詳解
- 常用的C++標(biāo)準(zhǔn)庫頭文件小結(jié)
- C++超詳細(xì)講解標(biāo)準(zhǔn)庫
- C++ STL標(biāo)準(zhǔn)庫std::vector的使用詳解
- C++ vector在多線程操作中出現(xiàn)內(nèi)存錯誤問題及解決
- C++多線程中互斥量的使用詳解
相關(guān)文章
C++動態(tài)分配和撤銷內(nèi)存以及結(jié)構(gòu)體類型作為函數(shù)參數(shù)
這篇文章主要介紹了C++動態(tài)分配和撤銷內(nèi)存以及結(jié)構(gòu)體類型作為函數(shù)參數(shù),是C++入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-09-09
C++哈希表之閉散列方法的模擬實(shí)現(xiàn)詳解
閉散列指(開放定址法)發(fā)生沖突時,如果哈希表沒有被填滿,則表內(nèi)一定還有其他空閑位置,可以把沖突值放到下一個沒有被占用的空余位置上。本文將模擬實(shí)現(xiàn)閉散列方法,需要的可以參考一下2022-11-11
C語言數(shù)據(jù)結(jié)構(gòu)與算法之排序總結(jié)(二)
這篇文章住要介紹的是選擇類排序中的簡單、樹形和堆排序,歸并排序、分配類排序的基數(shù)排序,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2021-12-12

