C++11線程使用總結
std::thread 在 <thread> 頭檔案中聲明,是以使用 std::thread 需包含 <thread> 頭檔案。
<thread> 頭檔案摘要
<thread> 頭檔案聲明了 std::thread 線程類及 std::swap (交換兩個線程對象)輔助函數。另外命名空間 std::this_thread 也聲明在 <thread> 頭檔案中。下面是 C++11 标準所定義的 <thread> 頭檔案摘要:
參見 N3242=11-0012 草案第 30.3 節 Threads(p1133)。
<b>[cpp]</b> view plain copy
amespace std {
#define __STDCPP_THREADS__ __cplusplus
class thread;
void swap(thread& x, thread& y);
namespace this_thread {
thread::id get_id();
void yield();
template <class Clock, class Duration>
void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);
template <class Rep, class Period>
void sleep_for(const chrono::duration<Rep, Period>& rel_time);
}
}
<thread> 頭檔案主要聲明了 std::thread 類,另外在 std::this_thread 命名空間中聲明了get_id,yield,sleep_until 以及 sleep_for 等輔助函數,本章稍微會詳細介紹 std::thread 類及相關函數。
std::thread 類摘要
std::thread 代表了一個線程對象,C++11 标準聲明如下:
namespace std {
class thread {
public:
// 類型聲明:
class id;
typedef implementation-defined native_handle_type;
// 構造函數、拷貝構造函數和析構函數聲明:
thread() noexcept;
template <class F, class ...Args> explicit thread(F&& f, Args&&... args);
~thread();
thread(const thread&) = delete;
thread(thread&&) noexcept;
thread& operator=(const thread&) = delete;
thread& operator=(thread&&) noexcept;
// 成員函數聲明:
void swap(thread&) noexcept;
bool joinable() const noexcept;
void join();
void detach();
id get_id() const noexcept;
native_handle_type native_handle();
// 靜态成員函數聲明:
static unsigned hardware_concurrency() noexcept;
};
std::thread 中主要聲明三類函數:(1). 構造函數、拷貝構造函數及析構函數;(2). 成員函數;(3). 靜态成員函數。另外,std::thread::id 表示線程 ID,同時 C++11 聲明如下:
class thread::id {
id() noexcept;
bool operator==(thread::id x, thread::id y) noexcept;
bool operator!=(thread::id x, thread::id y) noexcept;
bool operator<(thread::id x, thread::id y) noexcept;
bool operator<=(thread::id x, thread::id y) noexcept;
bool operator>(thread::id x, thread::id y) noexcept;
bool operator>=(thread::id x, thread::id y) noexcept;
template<class charT, class traits>
basic_ostream<charT, traits>&
operator<< (basic_ostream<charT, traits>& out, thread::id id);
// Hash 支援
template <class T> struct hash;
template <> struct hash<thread::id>;
std::thread 詳解
std::thread 構造和指派
std::thread 構造函數
預設構造函數 (1) thread() noexcept;
初始化構造函數 (2) template <class Fn, class... Args>
explicit thread(Fn&& fn, Args&&... args);
拷貝構造函數 [deleted] (3) thread(const thread&) = delete;
Move 構造函數 (4) thread(thread&& x) noexcept;
預設構造函數(1),建立一個空的 std::thread 執行對象。
初始化構造函數(2),建立一個 std::thread 對象,該 std::thread 對象可被 joinable,新産生的線程會調用 fn 函數,該函數的參數由 args 給出。
拷貝構造函數(被禁用)(3),意味着 std::thread 對象不可拷貝構造。
Move 構造函數(4),move 構造函數(move 語義是 C++11 新出現的概念,詳見附錄),調用成功之後 x 不代表任何std::thread 執行對象。
線程狀态:
在一個線程的生存期内,可以在多種狀态之間轉換,不同的作業系統可以實作不同的線程模型,定義許多不同的線程狀态,每個狀态還可以包含多個子狀态,但大體來說,如下幾種狀态是通用的:
1)就緒:參與排程,等待被執行,一旦被排程選中,立即開始執行
2)運作:占用CPU,正在運作中
3)休眠:暫不參與排程,等待特定事件發生
4)中止:已經運作完畢,等待回收線程資源
線程環境:
線程存在于程序之中,程序内所有全局資源對于内部每個線程都是可見的。
程序内典型全局資源如下:
1)代碼區:這意味着目前程序空間内所有的可見的函數代碼,對于每個線程來說,也是可見的
2)靜态存儲區:全局變量,靜态空間
3)動态存儲區:堆空間
線程内典型的局部資源:
1)本地棧空間:存放本線程的函數調用棧,函數内部的局部變量等
2)部分寄存器變量:線程下一步要執行代碼的指針偏移量
C++中的thread對象通常來說表達了執行的線程(thread of execution)。我在使用多線程的時候,發現很多情況下都是用join()函數,但是在使用detach的時候效果明顯就是不一樣了。
當thread::detach()函數被調用後,執行的線程從線程對象中被分離,該線程被從主線程分離出去放置到背景執行。已不再被一個線程對象所表達--這是兩個獨立的事情。C++線程對象可以被銷毀,同時OS執行的線程可以繼續。如果程式想要知道執行的線程何時結束,就需要一些其它的機制。join()函數在那個thread對象上不能再被調用,因為它已經不再和一個執行的線程相關聯。沒有thread對象指向該線程而失去了對它的控制,當對象析構時線程會繼續在背景執行,但是當主程式退出時并不能保證線程能執行完。如果沒有良好的控制機制或者這種背景線程比較重要,最好不用detach而應該使用join。
去銷毀一個仍然可以“joinable”的C++線程對象會被認為是一種錯誤。為了銷毀一個C++線程對象,約麼join()函數需要被調用(并結束),要麼detach()函數被調用。如果一個C++線程對象當銷毀時仍然可以被join,異常會被抛出。
mutex:
mutex是用來保證線程同步的,防止不同的線程同時操作同一個共享資料。
示例代碼:
int cnt= 20;
mutex m;
void t1()
{
while (cnt > 0)
{
m.lock();
if (cnt > 0)
{
--cnt;
cout << cnt << endl;
}
m.unlock();
void t2()
int main()
thread th1(t1);
thread th2(t2);
th1.join();
th2.join();
return 0;
運作結果,cnt是依次遞減的,沒有因為多線程而打亂次序:
lock_guard:
使用lock_guard則相對安全,它是基于作用域的,能夠自解鎖,當該對象建立時,它會像m.lock()一樣獲得互斥鎖,當生命周期結束時,它會自動析構(unlock),不會因為某個線程異常退出而影響其他線程。
int cnt = 20;
lock_guard<mutex> lockGuard(m);
get_id:
擷取線程 ID,傳回一個類型為 std::thread::id 的對象
示例如下:
#include <iostream>
#include <thread>
#include <chrono>
void foo()
std::this_thread::sleep_for(std::chrono::seconds(1));
std::thread t1(foo);
std::thread::id t1_id = t1.get_id();
std::thread t2(foo);
std::thread::id t2_id = t2.get_id();
std::cout << "t1‘s id: " << t1_id << ‘\n‘;
std::cout << "t2‘s id: " << t2_id << ‘\n‘;
t1.join();
t2.join();
sleep_until:
線程休眠至某個指定的時刻(time point),該線程才被重新喚醒。
template< class Clock, class Duration >
void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );
sleep_for:
線程休眠某個指定的時間片(time span),該線程才被重新喚醒,不過由于線程排程等原因,實際休眠時間可能比sleep_duration 所表示的時間片更長。
template< class Rep, class Period >
void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration );
std::cout << "waiter" << std::endl;
std::chrono::milliseconds dura( 1000 );
std::this_thread::sleep_for( dura );
std::cout << "Waited 1000 ms\n";