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的:
- 默认构造的对象
- 已经move给其他对象
- 已经执行过了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
注意
- 如果不可join而来调用join,抛异常
- 如果线程id是null,抛异常
- 本线程等本线程,相当于流程死锁,永远等不到结束,抛异常
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);
}