并发支持库(1)-线程

线程允许多个程序任务在统一时间执行,不同的线程可以共享内存空间,每个线程也有自己的栈空间。

本文章的代码库:

https://gitee.com/gamestorm577

线程类

thread

类thread表示单个执行线程。线程在thread构造对象时开始执行。每个thread对象表示唯一的一个线程,thread不支持复制构造和复制赋值函数。

构造函数

默认的构造函数构造一个不表示任何线程的thread对象:

thread() noexcept;

构造函数可以传入一个函数并关联一个执行线程,构造完成后函数将在该线程上开始运行:

template< class F, class... Args >
explicit thread( F&& f, Args&&... args );

代码示例:

auto func = [](int index)
{
    for (int i = 0; i < 15; ++i)
    {
        std::cout << index;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
};

std::thread t1(func, 1);
std::thread t2(func, 2);
t1.join();
t2.join();
std::cout << std::endl;

t1和t2分别在两个线程上同时执行,打印出来的1和2可能是交错的,可能的输出结果:

122121121212121212212121212121

析构函数

销毁thread对象时,必须合并或者分离底层线程,否则会调用std::terminate导致程序崩溃。其内部实现大致为:

~thread() noexcept 
{
    if (joinable()) 
    {
        terminate();
    }
}

赋值函数

thread只支持移动赋值函数,移动后线程的所有权被转交,原thread对象不再表示任何线程。

joinable

检查线程是否可合并,如果thread代表的线程是活跃的,那么joinable返回true。代码示例:

std::cout << std::boolalpha;

std::thread t;
std::cout << "t joinable: " << t.joinable() << std::endl;
t = std::thread(
    []()
    {
    });
std::cout << "t joinable: " << t.joinable() << std::endl;
t.join();
std::cout << "t joinable: " << t.joinable() << std::endl;

输出结果:

t joinable: false
t joinable: true
t joinable: false

get_id

获取线程的id。代码示例:

std::thread t1;
std::thread t2 = std::thread(
    []()
    {
    });
std::thread t3 = std::thread(
    []()
    {
    });

std::cout << "t1 id: " << t1.get_id() << std::endl;
std::cout << "t2 id: " << t2.get_id() << std::endl;
std::cout << "t3 id: " << t3.get_id() << std::endl;

t2.join();
t3.join();

输出结果:

t1 id: 0
t2 id: 97068
t3 id: 73212

native_handle

获取底层实现的线程句柄。代码示例:

std::thread t = std::thread(
    []()
    {
    });

std::thread::native_handle_type handle = t.native_handle();
std::cout << "native handle: " << handle << std::endl;
t.join();

输出结果:

native handle: 00000000000000A8

hardware_concurrency

返回系统的逻辑处理线程数。代码示例:

auto num = std::thread::hardware_concurrency();
std::cout << "hardware concurrency: " << num << std::endl;

可能的输出结果:

hardware concurrency: 8

join

阻塞thread代表的线程直到其执行结束。代码示例:

auto Func = []()
{
    std::cout << "t thread" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
};

std::thread t(Func);
t.join();
std::cout << "Done " << std::endl;

输出结果:

t thread
Done

detach

将thread管理的线程从thread中分离,thread不再代表任何线程。代码示例:

auto Func = []()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Func about to end" << std::endl;
};

std::thread t(Func);
t.detach();
std::cout << "t detach" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));

输出结果:

t detach
Func about to end

swap、std::swap

交换两个thread管理的线程。

jthread

和thread类似,jthread管理一个执行线程。不同的是,当jthread析构时会自动合并线程。jthread内部还有一个stop_source成员(stop_source包含stop_token对象),jthread可以接受一个用stop_token作为首参数的函数用于执行线程访问,jthread有一个request_stop接口用于修改stop_token的状态,这允许执行函数根据stop_token的状态来决定是否终止函数运行。

构造函数

构造一个jthread对象并关联一个执行线程(默认构造函数不关联任何线程)。代码示例:

auto func = [](int index)
{
    for (int i = 0; i < 15; ++i)
    {
        std::cout << index;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
};

std::jthread t1(func, 1);
std::jthread t2(func, 2);
t1.join();
t2.join();
std::cout << std::endl;

输出结果:

122112121212122121211212211212

析构函数

jthread对象析构时,会尝试合并其管理的执行线程,其内部实现大致为:

~jthread() 
{
    if (joinable()) 
    {
        request_stop();
        join();
    }
}

赋值函数

jthread只支持移动赋值函数,移动后线程的所有权被转交,原jthread对象不再表示任何线程。

joinable

检查线程是否可合并,如果thread代表的线程是活跃的,那么joinable返回true。

get_id

获取线程的id。

native_handle

获取底层实现的线程句柄。

hardware_concurrency

返回系统的逻辑处理线程数。

join

阻塞thread代表的线程直到其执行结束。

detach

将jthread管理的线程从jthread中分离,jthread不再代表任何线程。

swap、std::swap

交换两个jthread管理的线程。

停止记号处理

jthread提供了get_stop_source、get_stop_token用于获取stop_source和stop_token对象,requset_stop接口用于修改stop_token状态为停止,jthread关联的执行函数可以通过其第一个参数stop_token用于其状态。代码示例:

auto Func = [](std::stop_token token)
{
    for (int i = 0; i < 10; ++i)
    {
        if (token.stop_requested())
        {
            std::cout << i << " token stop_requested true" << std::endl;
        }
        else
        {
            std::cout << i << " token stop_requested false" << std::endl;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
};

std::jthread t(Func);
std::this_thread::sleep_for(std::chrono::milliseconds(350));
t.request_stop();

可能的输出结果:

0 token stop_requested false
1 token stop_requested false
2 token stop_requested false
3 token stop_requested false
4 token stop_requested true
5 token stop_requested true
6 token stop_requested true
7 token stop_requested true
8 token stop_requested true
9 token stop_requested true

注意最后一行代码

t.request_stop();

不是必须的,因为对象 t 在析构时也会调用request_stop函数。

当前线程管理函数

yield

重调度线程的执行,允许其他线程运行。具体效果依赖于编译器的实现。

get_id

返回当前线程的id。代码示例:

auto Func = []()
{
    std::cout << "thread id: " << std::this_thread::get_id() << std::endl;
};

std::thread t1(Func);
std::thread t2(Func);
Func();

t1.join();
t2.join();

输出结果:

thread id: 22388
thread id: 79692
thread id: 85280

sleep_for

阻塞当前线程一段时间。代码示例:

auto t1 = std::chrono::system_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(300));
auto t2 = std::chrono::system_clock::now();
std::chrono::duration<double, std::milli> time = t2 - t1;
std::cout << "time pass: " << time.count() << std::endl;

可能的输出结果:

time pass: 302.473

sleep_util

阻塞当前线程到指定的时间点。代码示例:

auto t1 = std::chrono::system_clock::now();
std::this_thread::sleep_until(t1 + std::chrono::milliseconds(400));
auto t2 = std::chrono::system_clock::now();
std::chrono::duration<double, std::milli> time = t2 - t1;
std::cout << "time pass: " << time.count() << std::endl;

可能的输出结果:

time pass: 400.504

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值