c++ 標準庫多線程問題小結
C++ 多線程編程允許程序同時執(zhí)行多個任務,從而提高性能和響應能力。C++11 引入了 <thread> 庫,使得多線程編程更加方便。以下是一些基本概念和示例,幫助你理解如何在 C++ 中進行多線程編程。
1. 創(chuàng)建線程
使用 std::thread 類可以創(chuàng)建一個新線程。你需要將一個函數(shù)或可調用對象傳遞給 std::thread 構造函數(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(); // 等待線程結束
std::cout << "Hello from main!\\\\n";
return 0;
}2. 傳遞參數(shù)給線程函數(shù)
你可以通過 std::thread 構造函數(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. 線程同步
多個線程可能會同時訪問共享資源,導致數(shù)據競爭。為了避免這種情況,可以使用互斥鎖(std::mutex)來保護共享資源。
#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 風格的簡單的鎖管理器,它在構造時自動加鎖,在析構時自動解鎖。
#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更靈活控制也更復雜重度的鎖:std::unique_lock。它可以RAII自動析構,也可以手動lock/unlock,中間有的代碼段就可以釋放鎖。手動把它unlock之后只是解鎖,沒有銷毀,后續(xù)可以按需復用再次 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; // 設置條件為 true
}
cv.notify_one(); // 通知等待的線程
t.join();
std::cout << "Hello from main!\\\\n";
return 0;
}
相比lock_guard的優(yōu)勢:
1. 靈活性:unique_lock 支持延遲鎖定(可以先構造對象而不立即加鎖),而 lock_guard 在構造時就必須加鎖。這意味著你可以先創(chuàng)建 unique_lock 對象,然后根據程序邏輯需要時再調用 lock() 或 unlock() 方法進行手動加鎖或解鎖。
2. 條件變量的支持:unique_lock 可以與標準庫中的條件變量一起使用,如 std::condition_variable,這是 lock_guard 所不具備的功能。這是因為條件變量需要能夠原子地釋放鎖并進入等待狀態(tài),這正是 unique_lock 提供的能力之一。
3. 鎖的所有權轉移:unique_lock 支持移動語義(move semantics),允許將鎖的所有權從一個 unique_lock 對象轉移到另一個對象,從而使得鎖可以在不同的作用域中傳遞。而 lock_guard 不支持這種操作,它的鎖所有權是固定的。
4. 嘗試鎖定(try-locking):除了基本的 lock() 和 unlock() 方法外,unique_lock 還提供了 try_lock() 方法,該方法嘗試獲取鎖但不會阻塞線程,如果無法獲得鎖則立即返回失敗結果。這對于避免線程長時間阻塞非常有用。
wait第二個參數(shù)predicate謂詞的用法參見:
<https://en.cppreference.com/w/cpp/thread/condition_variable/wait>
predicate不滿足不會結束等待執(zhí)行后續(xù)語句。6. 線程池
C++ 標準庫沒有直接提供線程池的實現(xiàn),但你可以使用第三方庫(如 Boost)或自己實現(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)允許每個線程擁有自己的變量實例。C++11 引入了 thread_local 關鍵字來實現(xià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. 異步任務
C++11 還引入了 std::async 和 std::future,用于異步執(zhí)行任務并獲取結果。
#include <iostream>
#include <future>
int compute() {
std::this_thread::sleep_for(std::chrono::seconds(2));
return 42;
}
int main() {
// 啟動異步任務
std::future<int> fut = std::async(std::launch::async, compute);
// 獲取結果
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í)行任務
std::thread t(std::move(task));
t.join();
// 獲取結果
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í)行任務
std::thread t(compute, std::move(prom));
// 獲取結果
int result = fut.get();
std::cout << "Result: " << result << std::endl;
t.join();
return 0;
}異步機制總結: C++11 的異步機制(std::future、std::async、std::packaged_task 和 std::promise)相比傳統(tǒng)的多線程編程,提供了以下額外的好處:
更高的抽象層次,簡化了異步操作的管理。
自動化的結果傳遞和異常處理。
更靈活的線程管理和任務執(zhí)行策略。
更清晰的代碼結構和更低的耦合度。
支持任務組合和超時等待。
這些機制使異步編程更加直觀、安全和高效,是現(xiàn)代 C++ 并發(fā)編程的重要組成部分。
總結
C++ 多線程編程提供了強大的工具來處理并發(fā)任務。通過使用 std::thread、std::mutex、std::condition_variable 等工具,你可以編寫高效且安全的多線程程序。需要注意的是,多線程編程容易引入數(shù)據競爭和死鎖等問題,因此需要仔細設計和測試。
到此這篇關于c++ 標準庫多線程的文章就介紹到這了,更多相關c++ 標準庫多線程內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
c++函數(shù)中的指針參數(shù)與地址參數(shù)區(qū)別介紹
c++函數(shù)中的指針參數(shù)與地址參數(shù)區(qū)別介紹;可供參考2012-11-11
Dashboard Interface 應用實現(xiàn)操作
Dashboard Server Remote Control Interface是一個關鍵的功能,它為用戶提供了通過TCP/IP協(xié)議遠程控制機器人的能力,執(zhí)行包括開關機、加載程序、檢查機器人狀態(tài)以及設置機器人操作模式等多種操作,本文介紹Dashboard Interface 應用操作,感興趣的朋友跟隨小編一起看看吧2024-08-08

