從 C++11 開始,標準庫里已經包含了對線程的支持,std::thread是C++11標準庫中的多線程的支持庫,pthread.h 是標準庫沒有添加多線程之前的在Linux上用的多線程庫。std::thread 是面向對象的多線程庫,使用簡單,推薦在項目中使用 std::thread 代替 pthread.h。
修改 CMakeLists.txt
項目中用到了C++ 17的時間代碼風格,需要修改為對應的版本。
# CMakeLists.txt
set(CMAKE_CXX_STANDARD 17)
創建線程
#include <iostream>
#include <thread>
using namespace std;
void SayHello() {
cout << "Hello World" << endl;
}
int main() {
std::thread t1(SayHello);
// 等待子線程結束才退出當前線程
pthread_exit(nullptr);
return 0;
}
如果不加 pthread_exit(nullptr),會報libc++abi: terminating
程序終止的錯誤??梢酝ㄟ^ detach() 函數,將子線程和主線分離,子線程可以獨立繼續運行,即使主線程結束,子線程也不會結束。
#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;
void SayHello() {
cout << "Hello World" << endl;
}
int main() {
std::thread t1(SayHello);
t1.detach();
this_thread::sleep_for(10ms);
// 低于C++17使用這行代碼 this_thread::sleep_for(chrono::milliseconds(10));
return 0;
}
傳遞參數
#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;
void SayHello(int id, string name) {
this_thread::sleep_for(10ms);
cout << "ID:" << id << ", Hello " << name << endl;
}
int main() {
std::thread t1(SayHello, 1, "Wiki");
t1.join();
return 0;
}
線程睡眠
using namespace std::literals::chrono_literals;
// 讓當前線程睡眠 10 毫秒
this_thread::sleep_for(10ms);
// 低于C++17使用這行代碼 this_thread::sleep_for(chrono::milliseconds(10));
// 讓當前線程睡眠 5 秒
this_thread::sleep_for(5s);
join() 等待線程運行結束
join() 函數可以在當前線程等待線程運行結束。
#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;
void SayHello() {
this_thread::sleep_for(10ms);
cout << "Hello World" << endl;
}
int main() {
std::thread t1(SayHello);
t1.join();
return 0;
}
condition_variable / wait / notify_one
使用 condition_variable 實現生產者和消費者的實驗,通過 wait 進入線程等待,知道有其它的線程把當前線程喚醒。
#include <iostream>
#include <thread>
#include <list>
using namespace std::literals::chrono_literals;
using namespace std;
std::mutex g_mutex;
condition_variable g_con;
list<int> products;
void test() {
int product_id = 0;
while (true) {
products.push_back(++product_id);
cout << "products 生產: " << product_id << endl;
std::unique_lock<std::mutex> lock(g_mutex);
// 通知消費者消費
g_con.notify_one();
lock.unlock();
if (product_id > 50) {
break;
}
this_thread::sleep_for(2ms);
}
}
int main() {
std::thread t1(test);
while (true) {
std::unique_lock<std::mutex> lock(g_mutex);
if (products.empty()) {
cout << "沒有產品,等待" << endl;
// 進入等待,知道有新產品
g_con.wait(lock);
} else {
int product_id = products.front();
products.pop_front();
cout << "消費產品 " << product_id << endl;
this_thread::sleep_for(2ms);
if (product_id > 50) break;
}
}
t1.join();
return 0;
}
輸出結果:
沒有產品,等待
products 生產: 1
消費產品 1
products 生產: 2
消費產品 2
沒有產品,等待
沒有產品,等待
...
thread_local
C++11中提供了thread_local,thread_local定義的變量在每個線程都保存一份副本,而且互不干擾,在線程退出的時候自動銷毀。
#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;
thread_local int t_l_counter = 0;
void test() {
cout << "flag1 t_l_counter: " << t_l_counter << endl;
t_l_counter = 2;
}
int main() {
t_l_counter = 1;
std::thread t1(test);
t1.join();
cout << "flag2 t_l_counter: " << t_l_counter << endl;
return 0;
}
結果:
flag1 t_l_counter: 0
flag2 t_l_counter: 1
同步鎖
#include <iostream>
#include <thread>
using namespace std;
void test() {
cout << "task start thread ID: " << this_thread::get_id() << endl;
this_thread::sleep_for(10ms);
cout << "task end thread ID: " << this_thread::get_id() << endl;
}
int main() {
std::thread t1(test);
std::thread t2(test);
std::thread t3(test);
t1.join();
t2.join();
t3.join();
return 0;
}
運行結果:
task start thread ID: task start thread ID: task start thread ID: 0x70000fab00000x70000fb33000
0x70000fbb6000
task end thread ID: 0x70000fab0000
task end thread ID: task end thread ID: 0x70000fb33000
0x70000fbb6000
以上代碼數據的結果是無序的,如果我們需要同一時間只有一個線程在test函數中執行代碼,那么就要加鎖,lock() 用于加鎖,而unlock() 解鎖。
#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;
std::mutex g_mutex;
void test() {
g_mutex.lock();
cout << "task start thread ID: " << this_thread::get_id() << endl;
this_thread::sleep_for(10ms);
cout << "task end thread ID: " << this_thread::get_id() << endl;
g_mutex.unlock();
}
int main() {
std::thread t1(test);
std::thread t2(test);
std::thread t3(test);
t1.join();
t2.join();
t3.join();
return 0;
}
運行結果:
task start thread ID: 0x70000e4f2000
task end thread ID: 0x70000e4f2000
task start thread ID: 0x70000e46f000
task end thread ID: 0x70000e46f000
task start thread ID: 0x70000e3ec000
task end thread ID: 0x70000e3ec000
除了std::mutex(非遞歸的互斥量),還有std::timed_mutex(帶超時的非遞歸互斥量),std::recursive_mutex(遞歸互斥量)、std::recursive_timed_mutex(帶超時的遞歸互斥量)。
同步鎖封裝類
可以創建一個 ScopeMutex 類,通過構造函數和析構函數實現加鎖和解鎖,ScopeMutex 的作用域只在 {} 之內加鎖。
#include <iostream>
#include <thread>
using namespace std::literals::chrono_literals;
using namespace std;
std::mutex g_mutex;
class ScopeMutex {
public:
explicit ScopeMutex(std::mutex &mutex) {
this->mutex = &mutex;
this->mutex->lock();
}
~ScopeMutex() {
this->mutex->unlock();
}
std::mutex *mutex;
};
void test() {
cout << "task prepare thread ID: " << this_thread::get_id() << endl;
{
ScopeMutex scopeMutex(g_mutex);
cout << "task start thread ID: " << this_thread::get_id() << endl;
this_thread::sleep_for(10ms);
cout << "task end thread ID: " << this_thread::get_id() << endl;
}
}
int main() {
std::thread t1(test);
std::thread t2(test);
std::thread t3(test);
t1.join();
t2.join();
t3.join();
return 0;
}
運行結果:
task prepare thread ID: task prepare thread ID: 0x70000d9bd0000x70000d8b7000
task start thread ID: 0x70000d9bd000
task prepare thread ID: 0x70000d93a000
task end thread ID: 0x70000d9bd000
task start thread ID: 0x70000d8b7000
task end thread ID: 0x70000d8b7000
task start thread ID: 0x70000d93a000
task end thread ID: 0x70000d93a000