• c++ -- thread详细解析


    thread的用法

    构造函数

    功能

    • 默认构造函数,初始化为空, 不表示任何执行线程
    • 传入函数对象_Fx和参数_Ax,构造一个线程对象,该对象表示一个可执行线程

    注意

    不允许拷贝构造和拷贝赋值。如果允许拷贝,那么拷贝以后,函数对象是重新执行,还是拷贝当前堆栈执行?join()应该等1个线程完成还是都完成?
    参见: https://stackoverflow.com/questions/35037035/why-c-threads-are-movable-but-not-copiable

    API

    thread() noexcept {
        // construct with no thread
        _Thr_set_null(_Thr);
    }
    
    /* 传入函数的构造函数, 具体没看懂,TODO, 
    而且    http://www.cplusplus.com/reference/thread/thread/thread/里有这个注意点:
    It also throws if the construction of any of the copies it makes (of the decay types of Fn and Args...) throws.  TODO
    */
    template<class _Fn, class... _Args, class = enable_if_t<!is_same_v<remove_cv_t<remove_reference_t<_Fn>>, thread>>>
    explicit thread(_Fn&& _Fx, _Args&&... _Ax) {
        // construct with _Fx(_Ax...)
        _Launch(&_Thr, _STD make_unique<tuple<decay_t<_Fn>, decay_t<_Args>...> >(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...));
    }
    
    thread(const thread&) = delete;
    thread& operator=(const thread&) = delete;
    

    移动构造和赋值

    功能

    • 传递一个右值引用,将入参_Other的线程对象move到该对象中,然后_Other设为空

    注意

    与移动构造函数类似,需要注意的是,因为是赋值而不是构造,因此可能本对象已经拥有线程对象了,此时再来赋值会触发terminate

    API

    thread(thread&& _Other) noexcept
        : _Thr(_Other._Thr)
    {
        _Thr_set_null(_Other._Thr);
    }
    
    thread& operator=(thread&& _Other) noexcept {
        return (_Move_thread(_Other));
    }
    
    thread& _Move_thread(thread& _Other)
    {	// move from _Other
        if (joinable())
            _STD terminate();
        _Thr = _Other._Thr;
        _Thr_set_null(_Other._Thr);
        return (*this);
    }
    

    析构函数

    功能

    • 资源清理

    注意

    如果对象析构时任务还在执行,触发terminate

    API

    ~thread() noexcept {    
        if (joinable())
            _STD terminate();
    }
    

    joinable

    功能

    • 是否代表了一个执行线程, 大致可以理解为是否有效

    注意

    下面情况不是可join的:

    1. 默认构造的对象
    2. 已经move给其他对象
    3. 已经执行过了join()或者deatch()

    API

    _NODISCARD bool joinable() const noexcept {
        // return true if this thread can be joined
        return (!_Thr_is_null(_Thr));
    }
    

    join

    功能

    等待线程执行结束, join和fork对应,fork表示分叉,join表示合并、汇聚。
    参考:https://stackoverflow.com/questions/9366264/what-does-it-mean-to-join-a-thread

    注意

    1. 如果不可join而来调用join,抛异常
    2. 如果线程id是null,抛异常
    3. 本线程等本线程,相当于流程死锁,永远等不到结束,抛异常

    API

    void join() {	
        // join thread
        if (!joinable())
            _Throw_Cpp_error(_INVALID_ARGUMENT);
        const bool _Is_null = _Thr_is_null(_Thr);	// Avoid Clang -Wparentheses-equality
        if (_Is_null)
            _Throw_Cpp_error(_INVALID_ARGUMENT);
        if (get_id() == _STD this_thread::get_id())
            _Throw_Cpp_error(_RESOURCE_DEADLOCK_WOULD_OCCUR);
        if (_Thrd_join(_Thr, nullptr) != _Thrd_success)
            _Throw_Cpp_error(_NO_SUCH_PROCESS);
        _Thr_set_null(_Thr);
    }
    

    detach

    功能

    分离线程,从此各自独立执行。detach会将自己维护的线程_Thr置为空,这样它就不可再join,相当于丧失了对该线程的管控,达到分离的目的

    注意

    不能对一个不可执行的对象执行分离操作

    建议

    分离线程后就丧失了对该线程的管控权,某些场景下会一定程度上简化代码实现,但是更多的时候可能埋下隐患。

    • 建议的使用场景:
      新建一个线程做异步任务,该任务一定会在不久后很快结束, 或者该线程不使用任何带有生命周期的对象(即使是单例对象也是有声明周期的, 如果在进程退出的过程中单例对象已经析构,而该线程仍在访问该单例对象,可能会导致进程崩溃)

    • 不建议的使用场景:
      新建的线程会长期停留,比如作为轮询任务一直存在。 这会导致我们彻底失去了对该线程的管控权,可能会导致各种并发问题。

    API

    void detach()
    {	// detach thread
        if (!joinable())
            _Throw_Cpp_error(_INVALID_ARGUMENT);
        _Thrd_detachX(_Thr);
        _Thr_set_null(_Thr);
    }
    

    get_id

    功能

    获得线程tid

    API

    _NODISCARD id get_id() const noexcept {
        return (_Thr_val(_Thr));
    }
    

    hardware_concurrency

    功能

    获得当前环境的硬件并发数,用来创建线程的时候作为参考

    注意

    这是一个静态方法,可以没有对象直接调用

    API

    _NODISCARD static unsigned int hardware_concurrency() noexcept
    {	// return number of hardware thread contexts
        return (_Thrd_hardware_concurrency());
    }
    

    native_handle

    功能

    返回底层具体实现的线程句柄,比如windows和linux底层对thread的实现一定不同。
    windows中,可以拿到该句柄,调用一些windows特有的接口,比如TerminateThread

    API

    _NODISCARD native_handle_type native_handle() {
        // return Win32 HANDLE as void *
        return (_Thr._Hnd);
    }
    

    this_thread的方法

    声明了一些可以在thread具体执行时调用的函数

    get_id

    功能

    获取当前执行线程的tid

    API

    _NODISCARD thread::id get_id() noexcept {
        return (_Thrd_id());
    }
    

    yield

    功能

    切换一次调度,防止长时间占用当前时间片

    API

    inline void yield() noexcept
    {	// give up balance of time slice
        _Thrd_yield();
    }
    

    sleep_until

    功能

    休眠到某个时间点, 跟的是一个绝对时间点

    API

    inline void sleep_until(const stdext::threads::xtime *_Abs_time) {	
        // sleep until _Abs_time
        _Thrd_sleep(_Abs_time);
    }
    
    template<class _Clock, class _Duration> 
    inline void sleep_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) {
        // sleep until time point
        this_thread::sleep_for(_Abs_time.time_since_epoch() - _Clock::now().time_since_epoch());
    }
    

    sleep_for

    功能

    休眠一段时间, 跟的是相对时间段

    API

    template<class _Rep, class _Period> 
    inline void sleep_for(const chrono::duration<_Rep, _Period>& _Rel_time) {
        // sleep for duration
        stdext::threads::xtime _Tgt = _To_xtime(_Rel_time);
        this_thread::sleep_until(&_Tgt);
    }
    
  • 相关阅读:
    uva 557 Burger
    uva 1639 Candy (对数处理精度)
    uva 10288 Coupons (分数模板)
    uva 12230 Crossing Rivers
    [USACO5.4] Telecowmunication
    epoll讲解--转自知乎
    多线程ExecutorService中submit和execute区别
    google Guava包的ListenableFuture解析
    git commit 时出现:please enter the commit message for your changes
    Git常用命令
  • 原文地址:https://www.cnblogs.com/mooooonlight/p/13811882.html
Copyright © 2020-2023  润新知