命名空间this_thread

命名空间 - this_thread

在C++11中不仅添加了线程类,还添加了一个关于线程的命名空间std::this_thread,在这个命名空间中提供了四个公共的成员函数,通过这些成员函数就可以对当前线程进行相关的操作了。

1.get_id()

调用命名空间std::this_thread中的get_id()方法可以得到当前线程的线程ID,函数原型如下:

thread::id get_id() noexcept;

关于函数使用对应的示例代码如下:

#include <iostream>
#include <thread>
using namespace std;

void func()
{
    cout << "子线程: " << this_thread::get_id() << endl;
}

int main()
{
    cout << "主线程: " << this_thread::get_id() << endl;
    thread t(func);
    t.join();
}

程序启动,开始执行main()函数,此时只有一个线程也就是主线程。当创建了子线程对象t之后,指定的函数func()会在子线程中执行,这时通过调用this_thread::get_id()就可以得到当前线程的线程ID了。

2.sleep_for()

线程和进程的执行有很多相似之处,在计算机中启动的多个线程都需要占用CPU资源,但是CPU的个数是有限的并且每个CPU在同一时间点不能同时处理多个任务。为了能够实现并发处理,多个线程都是分时复用CPU时间片,快速的交替处理各个线程中的任务。因此多个线程之间需要争抢CPU时间片,抢到了就执行,抢不到则无法执行(因为默认所有的线程优先级都相同,内核也会从中调度,不会出现某个线程永远抢不到CPU时间片的情况)。

命名空间this_thread中提供了一个休眠函数sleep_for(),调用这个函数的线程会马上从运行态变成阻塞态并在这种状态下休眠一定的时长,因为阻塞态的线程已经让出了CPU资源,代码也不会被执行,所以线程休眠过程中对CPU来说没有任何负担。这个函数是函数原型如下,参数需要指定一个休眠时长,是一个时间段:

template <class Rep, class Period>
  void sleep_for (const chrono::duration<Rep,Period>& rel_time);

示例代码:

#include <iostream>
#include <thread>
#include <chrono>
using namespace std;

void func()
{
    for (int i = 0; i < 10; ++i)
    {
        this_thread::sleep_for(chrono::seconds(1));
        cout << "子线程: " << this_thread::get_id() << ", i = " << i << endl;
    }
}

int main()
{
    thread t(func);
    t.join();
}

在func()函数的for循环中使用了this_thread::sleep_for(chrono::seconds(1));之后,每循环一次程序都会阻塞1秒钟,也就是说每隔1秒才会进行一次输出。需要注意的是:程序休眠完成之后,会从阻塞态重新变成就绪态,就绪态的线程需要再次争抢CPU时间片,抢到之后才会变成运行态,这时候程序才会继续向下运行。

3.sleep_until()

命名空间this_thread中提供了另一个休眠函数sleep_until(),和sleep_for()不同的是它的参数类型不一样

  • sleep_until():指定线程阻塞到某一个指定的时间点time_point类型,之后解除阻塞
  • sleep_for():指定线程阻塞一定的时间长度duration 类型,之后解除阻塞

该函数的函数原型如下:

template <class Clock, class Duration>
  void sleep_until (const chrono::time_point<Clock,Duration>& abs_time);

示例代码:

#include <iostream>
#include <thread>
#include <chrono>
using namespace std;

void func()
{
    for (int i = 0; i < 10; ++i)
    {
        // 获取当前系统时间点
        auto now = chrono::system_clock::now();
        // 时间间隔为2s
        chrono::seconds sec(2);
        // 当前时间点之后休眠两秒
        this_thread::sleep_until(now + sec);
        cout << "子线程: " << this_thread::get_id() << ", i = " << i << endl;
    }
}

int main()
{
    thread t(func);
    t.join();
}

sleep_until()和sleep_for()函数的功能是一样的,只不过前者是基于时间点去阻塞线程,后者是基于时间段去阻塞线程,项目开发过程中根据实际情况选择最优的解决方案即可。

4.yield()

命名空间this_thread中提供了一个非常绅士的函数yield(),在线程中调用这个函数之后,处于运行态的线程会主动让出自己已经抢到的CPU时间片,最终变为就绪态,这样其它的线程就有更大的概率能够抢到CPU时间片了。使用这个函数的时候需要注意一点,线程调用了yield()之后会主动放弃CPU资源,但是这个变为就绪态的线程会马上参与到下一轮CPU的抢夺战中,不排除它能继续抢到CPU时间片的情况,这是概率问题

void yield() noexcept;

函数对应的示例程序如下:

#include <iostream>
using namespace std;
#include <thread>
#include <chrono>

void test01() {
	for (int i = 0; i < 1000; i++) {
		cout << "子线程" << this_thread::get_id() << ", i = " << i << endl;
		this_thread::yield();
	}
}

int main() {
	thread t1(test01);
	for (int i = 0; i < 1000; ++i) {
		cout << "主线程" << this_thread::get_id() << ", " << i << endl;
	}
	t1.join();
	return 0;
}

这段程序, 主线程和子线程同时数数到1000, 子线程中调用了yield函数, 会在每次执行一次后出让线程, 因此主线程大概率会率先数到1000

结论:

  • std::this_thread::yield() 的目的是避免一个线程长时间占用CPU资源,从而导致多线程处理性能下降
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值