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)。
namespace 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 聲明如下:
namespace std {class thread::id {public: 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 執行對象。
注意:可被 joinable 的 std::thread 對象必須在他們銷毀之前被主線程 join 或者將其設置為 detached.
std::thread 各種構造函數例子如下(參考):
#include <iostream>#include <utility>#include <thread>#include <chrono>#include <functional>#include <atomic>
void f1(int n){for (int i = 0; i < 5; ++i) {std::cout << "Thread " << n << " executing\n";std::this_thread::sleep_for(std::chrono::milliseconds(10)); }}
void f2(int& n){for (int i = 0; i < 5; ++i) {std::cout << "Thread 2 executing\n"; ++n;std::this_thread::sleep_for(std::chrono::milliseconds(10)); }}
int main(){int n = 0;std::thread t1; std::thread t2(f1, n + 1); std::thread t3(f2, std::ref(n)); std::thread t4(std::move(t3)); t2.join(); t4.join();std::cout << "Final value of n is " << n << '\n';}std::thread 賦值操作
Move 賦值操作 (1)
thread& operator=(thread&& rhs) noexcept;拷貝賦值操作 [deleted] (2)thread& operator=(const thread&) = delete;Move 賦值操作(1),如果當前對象不可 joinable,需要傳遞一個右值引用(rhs)給 move 賦值操作;如果當前對象可被 joinable,則會調用 terminate() 報錯。
拷貝賦值操作(2),被禁用,因此 std::thread 對象不可拷貝賦值。
請看下面的例子:
#include <stdio.h>#include <stdlib.h>
#include <chrono> // std::chrono::seconds#include <iostream> // std::cout#include <thread> // std::thread, std::this_thread::sleep_for
void thread_task(int n) {std::this_thread::sleep_for(std::chrono::seconds(n));std::cout << "hello thread " << std::this_thread::get_id() << " paused " << n << " seconds" << std::endl;}
int main(int argc, const char *argv[]){std::thread threads[5];std::cout << "Spawning 5 threads...\n";for (int i = 0; i < 5; i++) { threads[i] = std::thread(thread_task, i + 1); }std::cout << "Done spawning threads! Now wait for them to join\n";for (auto& t: threads) { t.join(); }std::cout << "All threads joined.\n";
return EXIT_SUCCESS;}其他成員函數
本小節例子來自 http://en.cppreference.com
get_id: 獲取線程 ID,返回一個類型為 std::thread::id 的對象。請看下面例子:
#include <iostream>#include <thread>#include <chrono>
void foo(){std::this_thread::sleep_for(std::chrono::seconds(1)); }
int main(){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(); }joinable: 檢查線程是否可被 join。檢查當前的線程對象是否表示了一個活動的執行線程,由默認構造函數創建的線程是不能被 join 的。另外,如果某個線程 已經執行完任務,但是沒有被 join 的話,該線程依然會被認為是一個活動的執行線程,因此也是可以被 join 的。
#include <iostream>#include <thread>#include <chrono>
void foo(){std::this_thread::sleep_for(std::chrono::seconds(1)); }
int main(){std::thread t;std::cout << "before starting, joinable: " << t.joinable() << '\n';
t = std::thread(foo);std::cout << "after starting, joinable: " << t.joinable() << '\n';
t.join(); }join: Join 線程,調用該函數會阻塞當前線程,直到由 *this 所標示的線程執行完畢 join 才返回。
#include <iostream>#include <thread>#include <chrono>
void foo(){std::this_thread::sleep_for(std::chrono::seconds(1)); }
void bar(){std::this_thread::sleep_for(std::chrono::seconds(1)); }
int main(){std::cout << "starting first helper...\n";std::thread helper1(foo);
std::cout << "starting second helper...\n";std::thread helper2(bar);
std::cout << "waiting for helpers to finish..." << std::endl; helper1.join(); helper2.join();
std::cout << "done!\n"; }detach: Detach 線程。將當前線程對象所代表的執行實例與該線程對象分離,使得線程的執行可以單獨進行。一旦線程執行完畢,它所分配的資源將會被釋放。
調用 detach 函數之後:
*this 不再代表任何的線程執行實例。
joinable() == false
get_id() == std::this_thread::id()
另外,如果出錯或者 joinable() == false,則會拋出 std::system_error.
#include <iostream>#include <chrono>#include <thread>
void independentThread(){std::cout << "Starting concurrent thread.\n";std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "Exiting concurrent thread.\n"; }
void threadCaller(){std::cout << "Starting thread caller.\n";std::thread t(independentThread); t.detach();std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "Exiting thread caller.\n"; }
int main(){ threadCaller();std::this_thread::sleep_for(std::chrono::seconds(5)); }
執行結果如下:
thread 1 id: 1892
thread 2 id: 2584
after std::swap(t1, t2):
thread 1 id: 2584
thread 2 id: 1892
after t1.swap(t2):
thread 1 id: 1892
thread 2 id: 2584執行結果如下:
Thread 2 is executing at priority 0
Thread 1 is executing at priority 20hardware_concurrency [static]: 檢測硬體並發特性,返回當前平臺的線程實現所支持的線程並發數目,但返回值僅僅只作為系統提示(hint)。
#include <iostream>#include <thread>
int main() {unsigned int n = std::thread::hardware_concurrency();std::cout << n << " concurrent threads are supported.\n"; }std::this_thread 命名空間中相關輔助函數介紹
get_id: 獲取線程 ID。
#include <iostream> #include <thread> #include <chrono> #include <mutex> std::mutex g_display_mutex; void foo(){ std::thread::id this_id = std::this_thread::get_id(); g_display_mutex.lock(); std::cout << "thread " << this_id << " sleeping...\n"; g_display_mutex.unlock(); std::this_thread::sleep_for(std::chrono::seconds(1)); } int main(){ std::thread t1(foo); std::thread t2(foo); t1.join(); t2.join();yield: 當前線程放棄執行,作業系統調度另一線程繼續執行。
#include <iostream>#include <chrono>#include <thread>
void little_sleep(std::chrono::microseconds us){auto start = std::chrono::high_resolution_clock::now();auto end = start + us;do {std::this_thread::yield(); } while (std::chrono::high_resolution_clock::now() < end); }
int main(){auto start = std::chrono::high_resolution_clock::now();
little_sleep(std::chrono::microseconds(100));
auto elapsed = std::chrono::high_resolution_clock::now() - start;std::cout << "waited for " << std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count() << " microseconds\n"; }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 );
#include <iostream>#include <chrono>#include <thread>
int main(){std::cout << "Hello waiter" << std::endl;std::chrono::milliseconds dura( 2000 );std::this_thread::sleep_for( dura );std::cout << "Waited 2000 ms\n"; }執行結果如下:
Hello waiter
Waited 2000 ms