STL線程庫的前身是boost::thread,在C++ 11標准化后便正式歸納入了stl庫,通過它我們可以很容易實現跨平台的線程管理。
線程管理
在std::thread庫中,一個線程用的是一個thread對象表示,當創建一個thread對象時即創建一個線程,一個簡單的示例如下:
#include <iostream>
#include <thread>
using namespace std;
void thread_entry(const char* arg)
{
cout << "thread "<< this_thread::get_id() << " created: " << arg << endl;
}
int main()
{
thread thrd(thread_entry, "hello world");
thrd.join();
return 0;
}
這里我通過thread對象創建了一個線程,並調用join函數等待線程結束。thead對象的構造函數中是可以傳入參數的,非常方便。
std::this_thread名字空間
在前面的例子中,還用到了一個this_thread::get_id()函數用以獲取當前線程的tid,std::this_thread名字空間提供了如下函數以管理當前線程:
-
yield
-
get_id
-
sleep_for
-
sleep_until
基本上從名字里就可以猜出它們的功能了。以其中的sleep_for函數為例,它提供了一個跨平台的sleep功能,再也不用自己封裝了:
std::chrono::milliseconds dura(2000);
std::this_thread::sleep_for(dura);
PS:在gcc中使用這個函數時,需要再編譯的時候加-D_GLIBCXX_USE_NANOSLEEP選項,否則報語法錯誤,具體原因可以參看這篇文章。
Linux平台的運行錯誤
上述代碼在Windows平台運行良好,但在Linux上運行的時候發現如下錯誤:
tianfang > run
terminate called after throwing an instance of 'std::system_error'
what(): Operation not permitted
Aborted
tianfang >
然后在StackOverFlow上找到了答案:在鏈接的時候要加 –pthread 選項。這個應該算個bug了。
刪除Thread對象
我最初以為當thread對象刪除時會自動殺掉線程,運行時卻發現:thread對象刪除時,如果沒有調用join或attach,就會報異常。為了演示這一過程,首先我們把main函數改成如下所示:
int main()
{
thread thrd(thread_entry, "hello world");
//thrd.join();
return 0;
}
運行該代碼時,就會發現如下錯誤:
tianfang > run
terminate called without an active exception
Aborted
tianfang >
也就是說,thread對象並不會自動管理線程結束,需要手動控制。常見的控制手段有兩個,join和detach。join已經介紹過,用於等待線程結束,而detach的功能是托管線程,線程仍然繼續運行至結束,但不再受到thread對象控制。
對線程編程模型比較熟悉的人可能會發現:它並沒有提供一個強制終止線程的選項。在boost的this_thread名字空間下其實是提供了終止線程的函數的,但是並沒有納入stl中,可見標准委員會也是不建議強制終止這種不健壯的做法。
互斥和同步
互斥體
stl中對mutex划分得很細,按是否遞歸、是否超時分為四個對象:
-
mutex
-
timed_mutex
-
recursive_mutex
-
recursive_timed_mutex
這四個對象的操作函數大體上都是如下幾個:
-
lock 獲取鎖
-
trylock 嘗試獲取鎖
-
unlock 釋放鎖
-
try_lock_for 在一定時間范圍內嘗試獲取鎖(有超時功能的mutex才能用)
-
try_lock_until 嘗試獲取鎖到某個時間點位置(有超時功能的mutex才能用)
除了直接使用這幾個類外,也可以使用lock_guar、unique_lock之類的的封裝類,它的功能上類似C#的lock關鍵字(lock范圍並不等價),在指定范圍內自動獲取鎖,出了該范圍外自動解鎖,可有效防止加解鎖不對稱。
另外,stl也提供了try_lock和lock的泛型方法實現批量獲取鎖定,這里就不多介紹了。
條件變量
stl中對條件變量封裝的是condition_variable類,它的基本使用方式和系統api差不多,如下是一個生產者/消費者的例子:
#include <condition_variable>
#include <mutex>
#include <thread>
#include <iostream>
#include <queue>
#include <chrono>
using namespace std;
int main()
{
queue<int> buffer;
mutex m;
condition_variable cond_var;
int num = 0;
thread producer([&]()
{
while (true)
{
this_thread::sleep_for(chrono::seconds(1));
unique_lock<std::mutex> lock(m);
num++;
std::cout << "producing " << num << '\n';
buffer.push(num);
cond_var.notify_one();
}
});
thread consumer([&]()
{
while (true)
{
unique_lock<std::mutex> lock(m);
if(buffer.empty())
cond_var.wait(lock);
std::cout << "consuming " << buffer.front() << '\n';
buffer.pop();
}
});
producer.join();
consumer.join();
}
條件變量還有其它幾個封裝,這里就不多介紹了。
其它
其它幾個同步互斥的類,如柵欄、讀寫鎖等並沒有納入STL,如果要使用它們,可以直接使用boost庫。