線程
std::thread
創(chuàng)建線程
-
使用函數(shù)
#include <thread> #include <iostream> void task(int i) { std::cout << i << std::endl; } int main() { for (int i = 0; i < 8; i++) { std::thread t(task, i); t.detach(); } getchar(); return 0; }
-
使用lambda表達(dá)式創(chuàng)建線程
#include <thread> #include <iostream> int main() { for (int i = 0; i < 8; i++) { std::thread t([](int i){ std::cout << i << std::endl; }, i); t.detach(); } getchar(); return 0; }
-
重載了()運(yùn)算符的類的實(shí)例
#include <thread> #include <iostream> struct Task { void operator()(int i) { std::cout << i << std::endl; } }; int main() { for (int i = 0; i < 8; i++) { Task task; std::thread t(task, i); t.detach(); } getchar(); return 0; }
-
在類中創(chuàng)建線程
#include <iostream> #include <thread> #include <atomic> #include <chrono> class Worker { public: Worker() : stopFlag(false) {} // 啟動線程 void start() { workerThread = std::thread(&Worker::run, this); } // 停止線程 void stop() { stopFlag = true; if (workerThread.joinable()) { workerThread.join(); } } // 析構(gòu)函數(shù)確保線程停止 ~Worker() { stop(); } private: std::thread workerThread; std::atomic<bool> stopFlag; // 線程運(yùn)行的函數(shù) void run() { while (!stopFlag) { std::cout << "線程正在運(yùn)行..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } std::cout << "線程已停止" << std::endl; } }; int main() { Worker worker; worker.start(); // 啟動線程 std::this_thread::sleep_for(std::chrono::seconds(5)); // 主線程等待 worker.stop(); // 停止線程 return 0; }
參數(shù)傳遞
-
值傳遞
#include <thread> #include <iostream> struct Task { void operator()(int i) { printf("thread &i : %p\n", &i); } }; int main() { for (int i = 0; i < 1; i++) { printf("&i : %p\n", &i); Task task; std::thread t(task, i); t.detach(); } getchar(); return 0; }
-
引用傳遞
引用傳遞的地址和主線程的地址也是不一致的#include <thread> #include <iostream> struct Task { void operator()(const int &i) // 這里必須是const,不然會報(bào)錯(cuò) { printf("thread &i : %p\n", &i); } }; int main() { for (int i = 0; i < 1; i++) { printf("&i : %p\n", &i); Task task; std::thread t(task, i); t.detach(); } getchar(); return 0; }
-
指針傳遞
#include <thread> #include <iostream> struct Task { void operator()(int *i) { printf("thread &i : %p\n", i); } }; int main() { for (int i = 0; i < 1; i++) { printf("&i : %p\n", &i); Task task; std::thread t(task, &i); t.join(); // 這里使用join,確保i沒有被銷毀 } getchar(); return 0; }
-
std::ref 傳參
std::ref 可以保證子線程中的參數(shù)地址和主線程中的參數(shù)地址是一致的,即在子線程和主線程是同步的茶没。#include <thread> #include <iostream> struct Task { void operator()(int &i) { printf("thread &i : %p\n", &i); } }; int main() { for (int i = 0; i < 1; i++) { printf("&i : %p\n", &i); Task task; std::thread t(task, std::ref(i)); t.join(); } getchar(); return 0; }
鎖
std::mutex
- 構(gòu)造函數(shù)
- lock函數(shù) 調(diào)用線程將鎖住該互斥量土辩。
- unlock函數(shù) 調(diào)用線程將釋放該互斥量松蒜。
- try_lock函數(shù) 調(diào)用線程將嘗試鎖住該互斥量吼具,不成功返回false。
#include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex volatile int counter(0); // non-atomic counter std::mutex mtx; // locks access to counter void attempt_10k_increases() { for (int i=0; i<10000; ++i) { mtx.lock(); ++counter; mtx.unlock(); } } int main (int argc, const char* argv[]) { std::thread threads[10]; for (int i=0; i<10; ++i) threads[i] = std::thread(attempt_10k_increases); for (auto& th : threads) th.join(); std::cout << counter << " successful increases of the counter.\n"; return 0; }
std::lock_guard
自動鎖定和解鎖
當(dāng) std::lock_guard 對象被創(chuàng)建時(shí)嫂便,它會自動鎖定提供的互斥鎖。當(dāng)該對象離開其作用域(即被銷毀)時(shí)狮含,它會自動解鎖互斥鎖顽悼。這種自動管理確保了即使在發(fā)生異常的情況下,互斥鎖也能被正確解鎖几迄。簡單易用
std::lock_guard 的使用非常簡單蔚龙,只需要在需要保護(hù)的代碼塊之前創(chuàng)建它即可。無需顯式調(diào)用鎖定和解鎖函數(shù)映胁。-
非遞歸
std::lock_guard 不支持遞歸鎖定木羹,即不能多次鎖定同一個(gè)互斥鎖。如果嘗試這樣做解孙,會導(dǎo)致未定義的行為坑填。#include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex volatile int counter(0); // non-atomic counter std::mutex mtx; // locks access to counter void attempt_10k_increases() { for (int i=0; i<10000; ++i) { std::lock_guard<std::mutex> l(mtx); ++counter; } } int main (int argc, const char* argv[]) { std::thread threads[10]; for (int i=0; i<10; ++i) threads[i] = std::thread(attempt_10k_increases); for (auto& th : threads) th.join(); std::cout << counter << " successful increases of the counter.\n"; return 0; }
std::unique_lock
- 可以在構(gòu)造時(shí)選擇是否鎖定互斥量。
std::unique_lock<std::mutex> lock(my_mutex1, std::defer_lock);
std::defer_lock
的意思 就是 并沒有給mutex
加鎖:初始化一個(gè)沒有加鎖的mutex
弛姜,但是后面需要對unique_lock
對象進(jìn)行加鎖 - 支持手動鎖定和解鎖:可以在需要時(shí)調(diào)用
lock()
和unlock()
方法脐瑰。 - 支持條件變量的等待:在等待條件變量時(shí),可以傳入
unique_lock
廷臼,并在條件滿足時(shí)自動解鎖和重新鎖定苍在。#include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex volatile int counter(0); // non-atomic counter std::mutex mtx; // locks access to counter void attempt_10k_increases() { for (int i=0; i<10000; ++i) { std::unique_lock<std::mutex> l(mtx); ++counter; } } int main (int argc, const char* argv[]) { std::thread threads[10]; for (int i=0; i<10; ++i) threads[i] = std::thread(attempt_10k_increases); for (auto& th : threads) th.join(); std::cout << counter << " successful increases of the counter.\n"; return 0; }
條件變量
std::condition_variable
等待
條件變量的wait方法通常是通過以下方式實(shí)現(xiàn)的:
- 把線程放入等待隊(duì)列并釋放鎖绝页。
- 等待被顯式喚醒(notify_one 或 notify_all)或者因?yàn)樘摷賳拘驯徊僮飨到y(tǒng)強(qiáng)行喚醒。即使沒有執(zhí)行notify_one或者notify_all函數(shù)也有可能部分線程被虛假喚醒寂恬。
- 被喚醒后重新嘗試獲取鎖续誉。
void wait(std::unique_lock<std::mutex>& lock);
template <class Predicate>
void wait (unique_lock<mutex>& lck, Predicate pred); // 解決虛假喚醒
喚醒
void notify_one() noexcept;
void notify_all() noexcept;
DEMO
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
std::mutex mtx;
std::condition_variable cv;
bool ready = false; // 全局標(biāo)志位
void printId(int id)
{
std::unique_lock<std::mutex> lck(mtx);
// 如果標(biāo)志位不為true,則等待
while(!ready)
{
// 線程被阻塞初肉,直到標(biāo)志位變?yōu)閠rue酷鸦,此時(shí) mtx 被釋放,go 線程能夠獲取到鎖牙咏。
cv.wait(lck);
}
// 另一種寫法, 等價(jià)于上面的寫法
// cv.wait(lck, []{ return ready; });
std::cout << "thread: " << std::this_thread::get_id() << " id: " << id << "\n";
}
void go()
{
std::unique_lock<std::mutex> lck(mtx);
// 改變?nèi)謽?biāo)志位
ready = true;
// 喚醒所有線程
cv.notify_all();
}
int main()
{
std::thread threads[10];
for (int i = 0; i < 10; ++i)
{
threads[i] = std::thread(printId, i);
}
std::cout << "create done.\n" ;
go();
for (auto &t : threads)
{
t.join();
}
std::cout << "process done.\n" ;
return 0;
}
數(shù)據(jù)傳遞
promise future
promise future
承諾的未來
std::promise
的作用就是提供一個(gè)不同線程之間的數(shù)據(jù)同步機(jī)制臼隔,它可以存儲一個(gè)某種類型的值,并將其傳遞給對應(yīng)的future
眠寿, 即使這個(gè)future
不在同一個(gè)線程中也可以安全的訪問到這個(gè)值躬翁。
/** @file 20190815future.cpp
* @note
* @brief
* @author
* @date 2019-8-15
* @note
* @history
* @warning
*/
#include <iostream>
#include <functional>
#include <future>
#include <thread>
#include <chrono>
#include <cstdlib>
void thread_set_promise(std::promise<int>& promiseObj) {
std::cout << "In a thread, making data...\n";
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
promiseObj.set_value(35);
std::cout << "Finished\n";
}
int main() {
std::promise<int> promiseObj;
std::future<int> futureObj = promiseObj.get_future();
std::thread t(&thread_set_promise, std::ref(promiseObj));
std::cout << futureObj.get() << std::endl;
t.join();
system("pause");
return 0;
}
生產(chǎn)者消費(fèi)者
c++多線程實(shí)現(xiàn)生產(chǎn)者消費(fèi)者