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