标准库位置

作为一名C++的学习者,想要进阶,阅读源码是必不可少的

我们首先来看看源码的位置:首先引入头文件,然后按住CTRL点击进去,可以看到标准库的位置

Windows多线程及其相关标准库源码剖析(1):thread_c++


Windows多线程及其相关标准库源码剖析(1):thread_构造函数_02


下面来看看源码吧

Windows多线程及其相关标准库源码剖析(1):thread_ios_03

可见thread文件主要包括四个部分

  • thread类
  • 命名空间this_thread
  • thread的内部类id

解释部分

#if _HAS_CXX20
class jthread;
#endif // _HAS_CXX20

此处意思是如果是cpp20,则使用jthread类,此类在该头文件中也已经定义了,可见,其内容和thread其他方法差不多,在此不做过多概述

Windows多线程及其相关标准库源码剖析(1):thread_c++_04

thread类

class thread { // class for observing and managing threads
public:
// 内部类
class id;
// using的第二种用法,相当于typedef
using native_handle_type = void*;

接下来一个一个来看看其方法

构造函数

// 默认构造函数
thread() noexcept : _Thr{} {}
// 传入两个参数,一个函数名,一个参数
template <class _Fn, class... _Args, enable_if_t<!is_same_v<_Remove_cvref_t<_Fn>, thread>, int> = 0>
_NODISCARD_CTOR explicit thread(_Fn&& _Fx, _Args&&... _Ax) {
_Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
}
// 移动构造函数
thread(thread&& _Other) noexcept : _Thr(_STD exchange(_Other._Thr, {})) {}
// 拷贝构造函数
// C++11新语法,表示禁用拷贝构造
thread(const thread&) = delete;
// 示例
#include <iostream>
#include <thread>
using namespace std;

void func() {
cout << "Hello World" << endl;
}
void func_arg(int& n) {
cout << n << endl;
}
int main() {
thread t1(func); // 构造函数
int n = 10;
thread t3(func_arg, ref(n)); // 带参数构造函数
// 移动构造函数,此时执行的是t2,因为t1的线程函数移动给t2了
thread t2(move(t1));
t2.join();
t3.join();
return 0;
}

析构函数,joinable方法判断该线程是否可执行,如果是一个正在执行的线程,那么他是joinable的,有三种情况下他是不可joinable的

  • 默认构造函数构造的
  • 通过移动构造获得的
  • 正在detach或join的
~thread() noexcept {
if (joinable()) {
_STD terminate();
}
}
// 示例
#include <iostream>
#include <thread>
using namespace std;

void func() {
cout << "Hello World" << endl;
}
void func_arg(int& n) {
cout << n << endl;
}
int main() {
thread t1(func); // 构造函数
t1.~thread(); // 此时还在join状态就调用了~thread
t1.join();
return 0;
}
// 报错
Hello World
terminate called without an active exception

运算符重载:两个线程=(已删除)
必须要在joinable状态下进行操作,非joinable会报错

thread& operator=(thread&& _Other) noexcept {
if (joinable()) {
_STD terminate();
}

_Thr = _STD exchange(_Other._Thr, {});
return *this;
}
// 示例
// 没有示例了
// 这个玩意已经被删除了
thread& operator=(const thread&) = delete;

swap方法:交换两个线程的状态

void swap(thread& _Other) noexcept {
_STD swap(_Thr, _Other._Thr);
}
// 示例
#include <iostream>
#include <thread>
using namespace std;

void func() {
cout << "Hello World1" << endl;
}
void func2() {
cout << "Hello World2" << endl;
}
int main() {
thread t1(func); // 构造函数
thread t2(func2);
t2.swap(t2);
t2.join();
t1.join();
return 0;
}
// 输出
Hello World1Hello World2

joinable方法:判断线程是否可执行,前面已经讲解清楚了,在此就不做概述了,此处判断线程ID是否为0,即线程的ID是从1开始的,为0则表示不存在

_NODISCARD bool joinable() const noexcept {
return _Thr._Id != 0;
}

join方法:阻塞线程
join()是让用户手动管理线程,会阻塞当前线程直到*this所标识的线程结束,由于j.join()会阻塞主线程,所以j线程依旧会在主线程结束前结束

void join() {
if (!joinable()) {
_Throw_Cpp_error(_INVALID_ARGUMENT);
}

if (_Thr._Id == _Thrd_id()) {
_Throw_Cpp_error(_RESOURCE_DEADLOCK_WOULD_OCCUR);
}

if (_Thrd_join(_Thr, nullptr) != _Thrd_success) {
_Throw_Cpp_error(_NO_SUCH_PROCESS);
}

_Thr = {};
}

detach方法:分离线程
etach()不会阻塞当前线程,通过将thread对象分离线程,让线程独立执行,并且当线程运行结束后释放线程的所有资源。
detach()并不会阻塞主线程,所有在主线程结束后d线程依然没有结束,所以直到main函数退出,d线程中的Obj对象的析构函数还没有被调用。

void detach() {
if (!joinable()) {
_Throw_Cpp_error(_INVALID_ARGUMENT);
}

_Check_C_return(_Thrd_detach(_Thr));
_Thr = {};
}

get_id方法:获得线程的id

// 类内声明
_NODISCARD id get_id() const noexcept;
// 类外实现
_NODISCARD inline thread::id thread::get_id() const noexcept {
return _Thr._Id;
}
// 示例
#include <iostream>
#include <thread>
using namespace std;

void func() {
cout << "Hello World1" << endl;
}
void func2() {
cout << "Hello World2" << endl;
}
int main() {
thread t1(func); // 构造函数
thread t2(func2);
cout << t1.get_id() << endl;
cout << t2.get_id() << endl;
t2.join();
t1.join();
return 0;
}
// 运行结果
2
Hello World1
Hello World2
3

在此来介绍一下_Thr这个变量类型吧

// <thread>
private:
// 可知其是_Thrd_t变量类型
_Thrd_t _Thr;
// <xthreads.h>
/* 由前面的using用法可以知道,这个是重命名的意思,则_Thrd_id_t
* 是一个无符号整数
*/
using _Thrd_id_t = unsigned int;
// _Thrd_t是一个Win32线程标识符结构体
struct _Thrd_t { // thread identifier for Win32
void* _Hnd; // Win32 HANDLE
_Thrd_id_t _Id; // 线程的ID
};

hard_concurrency方法:获得当前线程最大数,如果当前值不可定义或不可精确,返回0

_NODISCARD static unsigned int hardware_concurrency() noexcept {
return _Thrd_hardware_concurrency();
}

native_handle方法:只有库实现支持时,这个成员函数才会出现在类thread中。
如果存在,则返回一个用于访问与线程相关的特定于实现的信息的值。

// using用法,native_handle_type是一个空指针
using native_handle_type = void*;
// native_handle方法
_NODISCARD native_handle_type native_handle() { // return Win32 HANDLE as void *
return _Thr._Hnd;
}
// 示例
#include <iostream>
#include <thread>
#include <ctime>
using namespace std;
void show(int n){
cout<<"n="<<n<<endl;
}
int main()
{
thread t(show,18);
cout<<"t.get_id="<<t.get_id()<<endl;
auto tn=t.native_handle(); // 可知此时输出的是线程的ID
t.join();
cout<<"tn="<<tn<<endl;
}

命名空间this_thread

get_id:获得线程的ID

namespace this_thread {
_NODISCARD thread::id get_id() noexcept;
// 示例
#include <iostream>
#include <thread>
using namespace std;

void func() {
cout << this_thread::get_id() << endl;
}
int main() {
thread t1(func); // 构造函数
t1.join();
return 0;
}
// 输出
2

yield:当前线程放弃执行,操作系统调度另一线程继续执行。即当前线程将未使用完的“CPU时间片”让给其他线程使用,等其他线程使用完后再与其他线程一起竞争"CPU"。

inline void yield() noexcept {
_Thrd_yield();
}

sleep_until:让线程等待到某个时间

// xtime格式
inline void sleep_until(const xtime* _Abs_time) {
_Thrd_sleep(_Abs_time);
}
// chrono的duration格式
template <class _Clock, class _Duration>
void sleep_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) {
for (;;) {
const auto _Now = _Clock::now();
if (_Abs_time <= _Now) {
return;
}

_CSTD xtime _Tgt;
(void) _To_xtime_10_day_clamped(_Tgt, _Abs_time - _Now);
_Thrd_sleep(&_Tgt);
}
}

sleep_for:让一个线程等待多少时间

template <class _Rep, class _Period>
void sleep_for(const chrono::duration<_Rep, _Period>& _Rel_time) {
sleep_until(_To_absolute_time(_Rel_time));
}
// 示例
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;

void func() {
this_thread::sleep_for(chrono::seconds(1));
cout << this_thread::get_id() << endl;
}
int main() {
thread t1(func);
t1.join();
return 0;
}

thread的内部类id

class thread::id { // thread id
public:
..

构造函数

// 无参构造
id() noexcept : _Id(0) {} // id for no thread
// 有参构造
id(_Thrd_id_t _Other_id) : _Id(_Other_id) {}

get_id:获取线程id

// 类内声明
friend thread::id thread::get_id() const noexcept;
// 类外实现
_NODISCARD inline thread::id thread::get_id() const noexcept {
return _Thr._Id;
}

get_id(this_thread):获取线程id

// 类内声明
friend thread::id this_thread::get_id() noexcept;
// 类外实现
_NODISCARD inline thread::id this_thread::get_id() noexcept {
return _Thrd_id();
}

运算符重载==:判断两个线程id是否一样

// 类内声明
friend bool operator==(thread::id _Left, thread::id _Right) noexcept;
// 类外实现
_NODISCARD inline bool operator==(thread::id _Left, thread::id _Right) noexcept {
return _Left._Id == _Right._Id;
}

运算符重载<:判断 两个线程id大小

// 类内声明
friend bool operator<(thread::id _Left, thread::id _Right) noexcept;
// 类外实现
_NODISCARD inline bool operator<(thread::id _Left, thread::id _Right) noexcept {
return _Left._Id < _Right._Id;
}

其他类似可见

_NODISCARD inline bool operator!=(thread::id _Left, thread::id _Right) noexcept {
return !(_Left == _Right);
}

_NODISCARD inline bool operator<=(thread::id _Left, thread::id _Right) noexcept {
return !(_Right < _Left);
}

_NODISCARD inline bool operator>(thread::id _Left, thread::id _Right) noexcept {
return _Right < _Left;
}

_NODISCARD inline bool operator>=(thread::id _Left, thread::id _Right) noexcept {
return !(_Left < _Right);
}

运算符重载<<:重载ostream输出流,输出时输出线程id

// 类内声明
template <class _Ch, class _Tr>
friend basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id);
// 类外实现
template <class _Ch, class _Tr>
basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id) {
return _Str << _Id._Id;
}

hash容器:std::thread::id类的std::hash模板特化允许用户获得线程标识符的哈希值。

// 类内声明
friend hash<thread::id>;
// 类外实现
// STRUCT TEMPLATE SPECIALIZATION hash
template <>
struct hash<thread::id> {
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef thread::id _ARGUMENT_TYPE_NAME;
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef size_t _RESULT_TYPE_NAME;

_NODISCARD size_t operator()(const thread::id _Keyval) const noexcept {
return _Hash_representation(_Keyval._Id);
}
};
// 示例
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;

void func() {
this_thread::sleep_for(chrono::seconds(1));
cout << this_thread::get_id() << endl;
}
int main() {
thread t1(func);
hash<thread::id> hasher;
// 获得线程标识符对应的哈希值
cout << hasher(t1.get_id()) << endl;
t1.join();
return 0;
}

thread到此结束。