(20) 函数 thrd_sleep(),该函数没有跟 C++ 的 STL 模板库直接关联。但也有类似之处,本函数处于更底层的位置:
++ 代码举例:
(21) 给出 thread 的精简版源代码,这样整理后,重点突出,更容易理解,只有 250 行左右:
// thread standard header
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#pragma once
#ifndef _THREAD_
#define _THREAD_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR
#include <__msvc_chrono.hpp>
#include <memory>
#include <process.h>
#include <tuple>
#include <xthreads.h>
#if _HAS_CXX20
#include <compare>
#include <stop_token>
#endif // _HAS_CXX20
#ifdef _M_CEE_PURE
#error <thread> is not supported when compiling with /clr:pure.
#endif // _M_CEE_PURE
#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
_STL_DISABLE_CLANG_WARNINGS
#pragma push_macro("new")
#undef new
// #define _STD_BEGIN namespace std {
// #define _STD_END }
_STD_BEGIN
#if _HAS_CXX20
class jthread; // 类声明 ,后面精简掉了其定义
#endif // _HAS_CXX20
using _Thrd_id_t = unsigned int; // 线程id
struct _Thrd_t {
void* _Hnd ; // windows 上的线程句柄,用于操作线程
_Thrd_id_t _Id ; // 全系统所有线程上的唯一线程 id,用于记录跟踪线程
};
class thread::id // 本类只包含线程 id,不含线程句柄
{
public:
id() : _Id(0) {} // 默认初始化线程 id 为 0
private:
_Thrd_id_t _Id; // _Thrd_id_t = unsigned int;
id(_Thrd_id_t _Other_id) : _Id(_Other_id) {}
friend thread::id thread::get_id() const noexcept; // 类的成员函数
friend thread::id this_thread::get_id() noexcept; // 命名空间里的全局函数
friend bool operator==(thread::id _Left, thread::id _Right) ;//比较是否为同一线程
};
class thread //本类的对象就是可执行函数代码的线程
{
private:
_Thrd_t _Thr; //兼具线程 id 与线程句柄的对象成员
public:
thread() : _Thr{} {} // 默认构造函数
bool joinable() { return _Thr._Id != 0; } // 验证创建了正确的线程, id 不为 0
~thread() { if ( joinable() ) _STD terminate(); } // 析构函数:若创建了线程则要关闭新线程
class id; // 本类只对 linux 系统有意义,定义在了类 thread 外面。
using native_handle_type = void*; // 这是 windows 的线程句柄
void join() // 本函只能执行一次
{ // 这只会导致主线程被阻塞,所以,此 join 函数在主线程中应靠后放置
if (!joinable()) _Throw_Cpp_error(_INVALID_ARGUMENT); // 报错
if ( _Thr._Id == _Thrd_id()) // 全局函数 _Thrd_id_t __cdecl _Thrd_id();
_Throw_Cpp_error(_RESOURCE_DEADLOCK_WOULD_OCCUR); // 出现资源死锁
// int __cdecl _Thrd_join(_Thrd_t, int*);
// _Thrd_join 函数会阻塞调用它的线程,直到指定的线程完成执行。
if (_Thrd_join(_Thr, nullptr) != _Thrd_success) // 报错可能是因为删除了报警的预编译# 语句
_Throw_Cpp_error(_NO_SUCH_PROCESS);
_Thr = {}; // 都有此行语句,join() 后本 thread 对象将不再管理有效的线程句柄与 id
}
void detach() // 调用本函后,子线程执行结束也会自动销毁,不会泄露系统资源
{
if (!joinable()) _Throw_Cpp_error(_INVALID_ARGUMENT);
// int __cdecl _Thrd_detach(_Thrd_t); 从此父线程不必等待子线程而睡眠了
_Check_C_return(_Thrd_detach(_Thr)); //当返回值不是 _Thrd_success 则报错
_Thr = {}; // 清空本 thread 对象管理的线程信息
}
void swap(thread& _Other) { _STD swap(_Thr, _Other._Thr); } //同时交换线程句柄与 id
//全局 swap() 函数支持的交换线程对象的功能,由本成员函数提供,交换 thread 对象的数据成员
id get_id() const noexcept { return _Thr._Id; } //返回 线程id,这是动态成员函数
// u_int _Thrd_hardware_concurrency(); 返回主机支持的并发线程数量,等于主机的 CPU逻辑核数
static unsigned int hardware_concurrency() { return _Thrd_hardware_concurrency(); }
native_handle_type native_handle() { return _Thr._Hnd; } // 获得 windows 线程句柄
private: //以下代码涉及线程的真正创建,涉及构造函数的方面
template <class _Tuple, size_t... _Indices> // 此函数会在新线程中执行
static unsigned int __stdcall _Invoke(void* _RawVals) // 类的静态函数
{
const unique_ptr<_Tuple> _FnVals(static_cast<_Tuple*>(_RawVals));
_Tuple& _Tup = *_FnVals; // 获得独占指针型的对象的地址
_STD invoke(_STD move(_STD get<_Indices>(_Tup))...); //调用函数导致执行代码
// void __cdecl _Cnd_do_broadcast_at_thread_exit();
_Cnd_do_broadcast_at_thread_exit(); // 广播,唤醒,线程同步,资源清理
return 0;
}
// 本函数并没有执行什么函数,只是返回了另一个函数 _Invoke 的地址
template <class _Tuple, size_t... _Indices> // 类的静态函数
static auto _Get_invoke(index_sequence<_Indices...>) // 逐步往上调用
{ return &_Invoke<_Tuple, _Indices...>; } // 将在线程中调用 _Invoke<T>() 函数
template <class _Fn, class... _Args>
void _Start(_Fn&& _Fx, _Args&&... _Ax) // 调用了上面函数
{ // decay 衰减,顾名思义,_Tuple 将包含最简单的数据类型,构成一个集合。
using _Tuple = tuple<decay_t<_Fn>, decay_t<_Args>...>; // 定义一种类型
auto _Decay_copied = // 这是在堆区生成一个 tuple 对象,并用独占指针指向之。
_STD make_unique<_Tuple> (_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
constexpr auto _Invoker_proc = //定义在上面,新线程里待执行的函数
_Get_invoke<_Tuple>(make_index_sequence<1 + sizeof...(_Args)>{});
// 获得线程句柄,设置本 thread 对象的数据成员 // 这里使用了 decay_copied,故这个 tuple 中将全是值传递 。
// u_long _beginthreadex( void* security, u_long stack_size, u_long (* start_address)(void*),
_Thr._Hnd = reinterpret_cast<void*>( //void* arglist, u_long initflag, u_long* thrdaddr );
_CSTD _beginthreadex(nullptr, 0, _Invoker_proc, _Decay_copied.get(), 0, &_Thr._Id)
); // unique_ptr<T>.get() 返回包含的裸指针
if (_Thr._Hnd) { // 创建新线程成功,将执行 if 分支
(void) _Decay_copied.release(); // unique_ptr<T>.release()使独占指针不再管理对象
} else { // failed to start thread // 新线程还要使用独占指针里的堆区对象里的参数
_Thr._Id = 0;
_Throw_Cpp_error(_RESOURCE_UNAVAILABLE_TRY_AGAIN);
}
}
public:
template <class _Fn, class... _Args, enable_if_t<!is_same_v<_Remove_cvref_t<_Fn>, thread>, int> = 0>
explicit thread(_Fn&& _Fx, _Args&&... _Ax) // 此处可知,万能引用还是引用。函数 Fx 执行时形参都是引用
{
_Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...); // 初始化时,立马调用上面的 start
}
thread(thread&& _Other) : _Thr(_STD exchange(_Other._Thr, {})) {} // 线程的移动构造函数
thread& operator=(thread&& _Other) // 线程的移动赋值运算符函数
{
if (joinable()) _STD terminate(); // 终止本线程正在执行的函数
_Thr = _STD exchange(_Other._Thr, {});
return *this;
}
thread(const thread&) = delete; // 禁止线程 thread 的 copy构造函数 与 copy赋值运算符函数
thread& operator=(const thread&) = delete;
};
inline void swap(thread& _Left, thread& _Right) noexcept // std 命名空间中的全局函数
{
_Left.swap(_Right);
}
// 接受 thread::id 类型的变量,但这样的变量只可以由 thread.get_id() 成员函数返回
inline bool operator==(thread::id _Left, thread::id _Right) noexcept
{
return _Left._Id == _Right._Id;
}
// // 此函数的返回值类型是 time_point < steady_clock , duration<long_long,nano> >
template <class _Rep, class _Period> // 此函数保留,因为下面的 this_thread ::
auto _To_absolute_time(const chrono::duration<_Rep, _Period>& _Rel_time) noexcept
{ // 顾名思义,本函数是要计算时间值,返回一个绝对时间值,且此事件值最好的是大于当前时间
constexpr auto _Zero = chrono::duration<_Rep, _Period>::zero();
const auto _Now = chrono::steady_clock::now(); // 高精度时钟返回本主机已经开机的时长
decltype(_Now + _Rel_time) _Abs_time = _Now; // return common type
if (_Rel_time > _Zero)
{
constexpr auto _Forever = (chrono::steady_clock::time_point::max)();
if (_Abs_time < _Forever - _Rel_time) _Abs_time += _Rel_time;
else _Abs_time = _Forever;
}
return _Abs_time;
}
namespace this_thread // 名称空间
{
thread::id get_id() noexcept { return _Thrd_id(); } // 功能等同于 thread.get_id(),但这是全局函数
inline void yield() noexcept { _Thrd_yield(); } // 使本线程睡眠
inline void sleep_until(const xtime* _Abs_time) { _Thrd_sleep(_Abs_time); }
template <class _Clock, class _Duration> // 可见,这里存在函数重载,一个普通函数,一个模板函数
void sleep_until(const chrono::time_point<_Clock, _Duration>& _Abs_time)
{
#if _HAS_CXX20
static_assert(chrono::is_clock_v<_Clock>, "Clock type required");
#endif // _HAS_CXX20
for (;;)
{
const auto _Now = _Clock::now();
if (_Abs_time <= _Now) return;
_CSTD xtime _Tgt;
// t_x_10_d_c() 的形参2 是时长,转换为时刻,存入形参 1
(void) _To_xtime_10_day_clamped(_Tgt, _Abs_time - _Now);
_Thrd_sleep(&_Tgt); // 所以,此函数的形参,是要睡眠到的时刻
}
}
template <class _Rep, class _Period> // 时间类以后再分析,要不用脑就太集中了
void sleep_for(const chrono::duration<_Rep, _Period>& _Rel_time)
{
sleep_until(_To_absolute_time(_Rel_time)); // 此定义在上面
}
} // namespace this_thread
_STD_END
#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _THREAD_
(22) 本 头文件里 thread 类可命名空间里的功能函数概述,这样就可以不用翻找源码了:
(23)
谢谢