vs2019 cpp20 规范的线程头文件 <thread> 注释( 5 ): 函 thrd_sleep (),给出 thread 的精简版源代码

(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)

谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值