在C++11中不仅添加了线程类,还添加了一个关于线程的命名空间
std::this_thread
在这个命名空间中提供了四个公共的成员函数
1. get_id()
所说曾相识,但还是有所不同 调用命名空间std::this_thread中的get_id()方法可以得到当前线程的线程ID
//原型 thread::id get_id() noexcept;
eg:
#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(); }
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);
参数需要指定一个休眠时长,是一个时间段
eg:
#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);
eg:
#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的抢夺中
//原型 void yield() noexcept;
eg:
#include <iostream> #include <thread> using namespace std; void func() { for (int i = 0; i < 100000000000; ++i) { cout << "子线程: " << this_thread::get_id() << ", i = " << i << endl; this_thread::yield(); } } int main() { thread t(func); thread t1(func); t.join(); t1.join(); }
在上面的程序中,执行func()中的for循环会占用大量的时间 极端情况下,如果当前线程占用CPU资源不释放就会导致其他线程中的任务无法被处理,或者该线程每次都能抢到CPU时间片,导致其他线程中的任务没有机会被执行。 解决方案就是每执行一次循环,让该线程主动放弃CPU资源,重新和其他线程再次抢夺CPU时间片,如果其他线程抢到了CPU时间片就可以执行相应的任务了。
结论:std::this_thread::yield()
的目的是避免一个线程长时间占用CPU资源,从而导致多线程处理性能下降