日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++ std::thread 和 std::jthread 使用详解 (含C++20新特性)

發布時間:2024/2/28 c/c++ 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++ std::thread 和 std::jthread 使用详解 (含C++20新特性) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

std::thread

std::thread 構造函數?

觀察器

操作

std::jthread

std::jthread 構造函數

觀察器

操作

停止記號處理

管理當前線程的函數

yield()

get_id()

sleep_for()

sleep_until()


C++ 11之前,官方并沒有支持線程庫。在Linux下完成多線程編程時,多數情況下是使用#include <pthread.h>的函數。C++ 11通過標準庫引入了對?thread 類的支持,大大方便了完成多線程開發的工作。在C++20中,引入的 jthread 類是 thread 自動合并和取消的實現版本。接下來將先從線程函數和 thread 類開始介紹,分析它們的不同,然后再介紹 jthread。

?

?

std::thread

?

std::thread 構造函數?

(1)thread()?noexcept; (2)thread(?thread&&?other?)?noexcept; (3)template<?class?Function,?class...?Args?>?explicit?thread(?Function&&?f, Args&&...?args?); (4)thread(const?thread&)?=?delete;

(1)?構造新?thread?對象,但由于沒有傳入函數,所以thread對象還沒有關聯到線程

(2)?移動構造函數。構造表示曾為?other?所表示的執行線程的?thread?對象。此調用后?other?不再表示執行線程。

(3)?構造新的?std::thread?對象并將它與執行線程關聯。新的執行線程開始執行

(4)?復制構造函數被刪除?thread?不可復制。

#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::thread t1; // t1 不是線程std::thread t2(f1, n + 1); // 按值傳遞std::thread t3(f2, std::ref(n)); // 按引用傳遞std::thread t4(std::move(t3)); // t4 現在運行 f2() 。 t3 不再是線程std::thread t5(&foo::bar, &f); // t5 在對象 f 上運行 foo::bar()std::thread t6(std::ref(b)); // t6 在對象 b 上運行 baz::operator()t2.join();t4.join();t5.join();t6.join();std::cout << "Final value of n is " << n << '\n';std::cout << "Final value of foo::n is " << f.n << '\n';std::cout << "Final value of baz::n is " << b.n << '\n'; }

注意:若需要傳遞引用參數給線程函數,則必須包裝它 (例如用 std::ref?或?std::cref)。忽略來自函數的任何返回值。若函數拋異常,則調用?std::terminate。

?

pthread_create 線程創建函數

int?pthread_create?(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

功能:創建一個具有指定參數的線程。

形參:thread 是要創建的線程的線程ID指針。

pthread_t 類型的定義是 typedef unsigned long int pthread_t;(打印時要使用%lu或%u方式)。

attr:創建線程時的線程屬性(設置NULL表示使用默認線程屬性)。

start_routine:指向的是新線程將運行的函數。線程一旦被創建好,內核就可以調度內核線程來執行 start_routine 函數指針所指向的函數了。

arg:指向的是運行函數的形參。

返回值:若是成功建立線程返回0,否則返回錯誤的編號。

相對而言,使用線程創建函數更為復雜。但這并不意味這就不使用 pthread 提供的函數了,因為 pthread 中的函數可以設置線程的屬性。這在某些情況下是非常有用的,所以有時會將兩者結合起來使用。

?

?

觀察器

joinable

bool?joinable()?const?noexcept;

用于判斷?thread?對象是否關聯到某一線程,若 thread 對象執行線程關聯,返回?true?,反之為?false?。

#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: " << 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

std::thread::id?get_id()?const?noexcept;

返回標識與?*this?關聯的線程的 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(); }t1's id: 0x35a7210f t2's id: 0x35a311c4

?

?

操作

join

void?join();

阻塞當前線程直至?*this?所標識的線程結束其執行。*this?所標識的線程的完成同步于對應的從?join()?成功返回。*this?自身上不進行同步。同時從多個線程在同一 thread 對象上調用?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"; }starting first helper... starting second helper... waiting for helpers to finish... done!

?

pthread_join 等待線程結束函數

int?pthread_join(pthread_t thread, void **retval);

功能:這個函數是一個線程阻塞的函數,調用它的函數將一直等待到被等待的線程結束為止,當函數返回時,被等待線程的資源被收回。

形參:thread是被等待的線程標識符。

retval:一個用戶定義的指針,它可以用來存儲被等待線程的返回值。

返回值:成功返回0,否則返回錯誤的編號。

錯誤碼:

①EDEADLK:可能引起死鎖,比如兩個線程互相針對對方調用pthread_join,或者線程對自身調用pthread_join。

②EINVAL:目標線程是不可回收的,或者已經有其他線程在回收目標線程。

③ESRCH:目標線程不存在。

?

detach

void?detach();

從 thread 對象分離執行線程,允許執行獨立地持續。一旦該線程退出,則釋放任何分配的資源。調用?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::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)); }Starting thread caller. Starting concurrent thread. Exiting thread caller. Exiting concurrent thread.

?

pthread_detach 分離釋放線程函數

int?pthread_detach?(pthread_t thread);

功能:允許分離執行線程,即獨立的執行。也是線程資源釋放的一種方式,一旦線程退出,則釋放分配的資源。

形參:thread 是要釋放線程的標識符 Id

返回值:若是成功返回 0,否則返回錯誤的編號。

其他說明:linux 線程執行和 windows 不同,pthread 有兩種狀態 joinable 狀態和 unjoinable 狀態。一個線程默認的狀態是 joinable,如果線程是 joinable 狀態,當線程函數自己返回退出時或 pthread_exit 時,都不會釋放線程所占用堆棧和線程描述符(總計8K多),只有當調用了 pthread_join 之后這些資源才會被釋放。若是 unjoinable 狀態的線程,這些資源在線程函數退出時或 pthread_exit 時自動會被釋放。unjoinable 屬性可以在 pthread_create 時指定,或在線程創建后在線程中 pthread_detach 自己設置,如:pthread_detach( pthread_self() ),將狀態改為 unjoinable 狀態,確保資源的釋放。如果線程狀態為 joinable ,需要在之后適時調用 pthread_join。

?

swap

void?swap(?std::thread&?other?)?noexcept;

交換二個 thread 對象的底層柄。

#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::thread t1(foo);std::thread 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';t1.join();t2.join(); }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

?

總結:

(0x01) std::thread 類創建線程非常方便,構造 thread 對象時傳入一個需要運行的函數及其參數。構造完成后,新的線程馬上被創建,同時執行該對象。注意:若需要傳遞引用參數給線程函數,則必須包裝它(例如用?std::ref?或?std::cref)。

(0x02) 使用 std::thread 默認的構造函數構造對象時,該對象是不關聯任何線程的。可以在之后的使用過程中再關聯到某一線程。可以通過使用 joinable() 接口,判斷一個 thread 對象是否關聯某個線程。

(0x03)?關聯到線程的 thread 對象析構前,必須調用 join() 接口等待線程結束。或者 thread 對象調用 detach() 接口解除與線程的關聯,否則會拋異常。

(0x04) thread 對象 detach() 后會獨立執行直至結束,而對應的 thread 對象變成不關聯任何線程的對象,joinable() 將返回 false。

(0x05) std::thread 沒有拷貝構造函數和拷貝賦值操作符,因此不支持復制操作(但從構造函數的示例代碼可以看出 std::thread 可以 move )。這也說明了沒有兩個 thread 對象可以表示同一執行線程。

?

?

std::jthread

它擁有同 std::thread?的行為主要增加了以下兩個功能:

(1)?std::jthread 對象被 destruct 時,會自動調用 join,等待其所表示的執行流結束。

(2)?支持外部請求中止(通過 get_stop_source、get_stop_token 和 request_stop )。

?

為什么不是選擇往 std::thread 添加新接口,而是引入了一個新的標準庫?

因為 std::jthread 為了實現上述新功能,帶來了額外的性能開銷 (主要是多了一個成員變量)。而根據 C++ 一直以來?"不為不使用的功能付費"?的設計哲學,他們自然就把這些新功能拆出來新做了一個類。

?

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++14std::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?之后,由于調度或資源爭議延遲。

?

?

參考:https://zh.cppreference.com/w/cpp/thread

https://www.zhihu.com/question/364140779

https://blog.csdn.net/qq_38289815/article/details/82950320

總結

以上是生活随笔為你收集整理的C++ std::thread 和 std::jthread 使用详解 (含C++20新特性)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。