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);
}
posted @ 2020-10-14 15:41  mooooonlight  阅读(719)  评论(0编辑  收藏  举报