在C++20中新加了jthread類,jthread是對thread的一種封裝
std::jthread 構造函數
(1)jthread() noexcept;
(2)jthread( jthread&& other ) noexcept;
(3)template< class Function, class... Args >
explicit jthread( Function&& f, Args&&... args );
(4)jthread( const jthread& ) = delete;
(1) 構造新的 jthread 對象,但由於沒有傳入函數,所以 jthread 對象還沒有關聯到線程。
(2) 移動構造函數。構造的 jthread 對象表示之前由 other 表示的執行線程。此調用后 other 不再表示執行線程。
(3) 創建與執行線程關聯的新 std::jthread 對象。若函數 f 接受 std::stop_token 作為其首參數,則新線程開始執行。
(4) 復制構造函數被刪除;線程不可復制。
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
void f1(int n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 1 executing\n";
++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));
}
}
class foo
{
public:
void bar()
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 3 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int n = 0;
};
class baz
{
public:
void operator()()
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 4 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int n = 0;
};
int main()
{
int n = 0;
foo f;
baz b;
std::jthread t0; // t0 不是線程
std::jthread t1(f1, n + 1); // 按值傳遞
std::jthread t2a(f2, std::ref(n)); // 按引用傳遞
std::jthread t2b(std::move(t2a)); // t2b 現在運行 f2() 。 t2a 不再是線程
std::jthread t3(&foo::bar, &f); // t3 在對象 f 上運行 foo::bar()
std::jthread t4(b); // t4 在對象 b 上運行 baz::operator()
t1.join();
t2b.join();
t3.join();
std::cout << "Final value of n is " << n << '\n';
std::cout << "Final value of foo::n is " << f.n << '\n';
// t4 在析構時結合
}
觀察器
joinable
[[nodiscard]] bool joinable() const noexcept;
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::jthread t;
std::cout << "before starting, joinable: " << std::boolalpha << t.joinable()
<< '\n';
t = std::thread(foo);
std::cout << "after starting, joinable: " << t.joinable()
<< '\n';
t.join();
std::cout << "after joining, joinable: " << t.joinable()
<< '\n';
}
輸出信息:
before starting, joinable: false
after starting, joinable: true
after joining, joinable: false
get_id
[[nodiscard]] std::jthread::id get_id() const noexcept;
返回標識與 *this 關聯的線程的 std::jthread::id 。
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::jthread t1(foo);
std::jthread::id t1_id = t1.get_id();
std::jthread t2(foo);
std::jthread::id t2_id = t2.get_id();
std::cout << "t1's id: " << t1_id << '\n';
std::cout << "t2's id: " << t2_id << '\n';
}
輸出信息:
t1's id: 0x35a7210f
t2's id: 0x35a311c4
操作
join
void join();
阻塞當前線程直至 *this 所標識的線程結束其執行。*this 所標識的線程的完成同步於對應的從 join() 成功返回。*this 自身上不進行同步。同時從多個線程在同一 jthread 對象上調用 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::jthread helper1(foo);
std::cout << "starting second helper...\n";
std::jthread helper2(bar);
std::cout << "waiting for helpers to finish..." << std::endl;
helper1.join();
helper2.join();
std::cout << "done!\n";
}
輸出信息:
starting first helper...
starting second helper...
waiting for helpers to finish...
done!
detach
void detach();
從 jthread 對象分離執行線程,允許線程獨立地運行。一旦該線程退出,則釋放任何分配的資源。調用 detach 后 *this 不再占有任何線程。
#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::jthread 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));
}
輸出信息:
Starting thread caller.
Starting concurrent thread.
Exiting thread caller.
Exiting concurrent thread.
swap
void swap( std::jthread& other ) noexcept;
交換二個 jthread 對象的底層柄。
#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::jthread t1(foo);
std::jthread t2(bar);
std::cout << "thread 1 id: " << t1.get_id() << '\n'
<< "thread 2 id: " << t2.get_id() << '\n';
std::swap(t1, t2);
std::cout << "after std::swap(t1, t2):" << '\n'
<< "thread 1 id: " << t1.get_id() << '\n'
<< "thread 2 id: " << t2.get_id() << '\n';
t1.swap(t2);
std::cout << "after t1.swap(t2):" << '\n'
<< "thread 1 id: " << t1.get_id() << '\n'
<< "thread 2 id: " << t2.get_id() << '\n';
}
輸出信息:
thread 1 id: 140185268262656
thread 2 id: 140185259869952
after std::swap(t1, t2):
thread 1 id: 140185259869952
thread 2 id: 140185268262656
after t1.swap(t2):
thread 1 id: 140185268262656
thread 2 id: 140185259869952
停止記號處理
get_stop_source
std::stop_source get_stop_source() const noexcept;
返回 std::stop_source ,擁有與 jthread 對象內部所保有者相同的共享停止狀態。
get_stop_token
std::stop_token get_stop_token() const noexcept;
返回 std::stop_token ,與 jthread 對象內部保有的同一共享停止狀態關聯。
request_stop
bool request_stop() noexcept;
若內部停止狀態尚未被請求停止,則對它發出停止請求。原子地作出確定,而若請求了停止,則原子地更新共享狀態以避免競爭條件,使得:能在同一共享狀態的 std::stop_token 與 std::stop_source 上同時調用 stop_requested() 與 stop_possible() 能從多個線程在同一 jthread 對象或與同一停止狀態關聯的其他 std::stop_source 對象上並發調用 request_stop() ,而將只有一個線程實際進行停止請求。
注意:若 request_stop() 發出停止請求(即返回 true ),則將在發出 request_stop() 的同一線程上同步調用對同一共享停止狀態注冊的任何 std::stop_callbacks 。若任何回調的調用經由異常退出,則調用 std::terminate 。
若已作出停止請求,則此函數返回 false 。然而不保證正好對同一停止狀態(成功)請求停止的另一線程或 std::stop_source 對象不仍然在調用 std::stop_callback 函數的中間。
若 request_stop() 發出停止請求(即返回 true ),則提醒所有用與 jthread 的內部停止狀態關聯的 stop_token 的可中斷等待注冊的、基類型為 std::condition_variable_any 的條件變量。
管理當前線程的函數
yield()
void yield() noexcept;
提供提示給實現,以重調度線程的執行,允許其他線程運行。
#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";
}
輸出信息:
waited for 128 microseconds
get_id()
std::thread::id get_id() noexcept;
#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();
}
輸出信息:
thread 0x2384b312 sleeping...
thread 0x228a10fc sleeping...
sleep_for()
template< class Rep, class Period >
void sleep_for( const std::chrono::duration<Rep, Period>& sleep_duration );
阻塞當前線程執行,至少經過指定的 sleep_duration 。此函數可能阻塞長於 sleep_duration ,因為調度或資源爭議延遲。標准庫建議用穩定時鍾度量時長。若實現用系統時間代替,則等待時間亦可能對時鍾調節敏感。
#include <iostream>
#include <chrono>
#include <thread>
int main()
{
using namespace std::chrono_literals; // C++14
std::cout << "Hello waiter" << std::endl; // 有意沖入
auto start = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(2s);
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end-start;
std::cout << "Waited " << elapsed.count() << " ms\n";
}
輸出信息:
Hello waiter
Waited 2000.12 ms
sleep_until()
template< class Clock, class Duration >
void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );
阻塞當前線程,直至抵達指定的 sleep_time 。使用聯傾向於 sleep_time 的時鍾,這表示時鍾調節有影響。從而在調用時間點后,阻塞的時長可能小於,但不會多於 sleep_time - Clock::now() 。函數亦可能阻塞長於抵達 sleep_time 之后,由於調度或資源爭議延遲。