C++20 多線程 std::jthread


在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 之后,由於調度或資源爭議延遲。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM