thread的内部类;
thread的构造函数;
执行流程;
一些对外函数的实现;
引言
如果写一段这样的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <bits/stdc++.h> struct node { node () { printf ("%p, cons\n" , this ); } node (node &n) { printf ("%p, copy from %p\n" , this , &n); } node (node &&n) { printf ("%p, move from %p\n" , this , &n); } ~node () { printf ("%p,dest\n" , this ); } };void fun (struct node nd) { std::this_thread::sleep_for (std::chrono::seconds (5 )); printf ("%p\n" , &nd); }int main () { printf ("1\n" ); std::thread t (fun, node()) ; printf ("2\n" ); t.join (); printf ("3\n" ); return 0 ; }
编译命令为:
输出结果将会类似这样:
1 2 3 4 5 6 7 8 9 10 11 12 1 0x7fff2e724a3f , cons0x7fff2e7249f8 , move from 0x7fff2e724a3f 0x5589443292c8 , move from 0x7fff2e7249f8 0x7fff2e7249f8 ,dest0x7fff2e724a3f ,dest2 0x7fd6a8499df7 , move from 0x5589443292c8 0x7fd6a8499df7 0x7fd6a8499df7 ,dest0x5589443292c8 ,dest3
可以看出,一开始构造了一个对象位于栈内存0x7fff2e724a3f
.
后来他被移动构造到栈内存0x7fff2e7249f8
,
再后来他被移动到堆内存0x5589443292c8
,
然后按照先构造后析构的顺序,析构了0x7fff2e7249f8
, 0x7fff2e724a3f
.
线程执行之后,他被移动到栈内存0x7fd6a8499df7
,等待五秒并打印了这个地址.
函数执行结束,析构了0x7fd6a8499df7
.
在线程结束时,析构了0x5589443292c8
.
看完下面文章,你会清楚这些构造,移动和析构,都在什么函数,什么时机中执行.
文件开头定义
宏
1 2 #ifndef _GLIBCXX_THREAD #define _GLIBCXX_THREAD 1
定义_GLIBCXX_THREAD
宏.
1 #pragma GCC system_header
从#pragma GCC system_header
直到文件结束之间的代码会被编译器视为系统头文件之中的代码。系统头文件中的代码往往不能完全遵循C标准, 所以头文件之中的警告信息往往不显示。(除非用#warning
显式指明)。
1 2 3 #if __cplusplus < 201103L # include <bits/c++0x_warning.h> #else
__cpluscplus
表示当前的c++
标准,主要有这几个值
值
标准
199711L
c++98
201103L
c++11
201402L
c++14
201703L
c++17
202002L
c++20
202100L
c++23(不完整)
显然他们是单调递增的,c++11
之前的__cpluscplus
都会小于201103L
.
这里表示如果当前标准小于c++11
,那么不引入下面的代码,而是直接引入<bits/c++0x_warning.h>
.
头文件
1 2 3 4 5 6 7 8 #include <chrono> #include <memory> #include <tuple> #include <cerrno> #include <bits/functexcept.h> #include <bits/functional_hash.h> #include <bits/invoke.h> #include <bits/gthr.h>
作用如下:
头文件
作用
chrono
时间日期相关
memory
内存分配,回收和管理
tuple
元组
cerrno
里面包含了error.h
,定义了errno
的整数值
bits/functexcept.h
定义了一些内部函数,与抛出异常有关
bits/functional_hash.h
定义了一些哈希相关的类,hash
函数最终调用了bits/hash_bytes.h
的_Hash_bytes
bits/invoke.h
实现了__invoke
类,用来调用某个可调用对象
bits/gthr.h
引入了bits/gthr-default.h
,这个宏里面定义了thread
所需的posix
类型
1 #if defined(_GLIBCXX_HAS_GTHREADS)
在bits/c++config.h
中定义如果gthread
库可用,值为1
.
可见性
1 2 3 namespace std _GLIBCXX_VISIBILITY(default ) { _GLIBCXX_BEGIN_NAMESPACE_VERSION
命名空间的可见性为默认,实体在共享库中可见,并且可以被抢占.
这个_GLIBCXX_VISIBILITY
定义如下:
1 2 3 4 5 6 7 8 9 10 11 #ifndef _GLIBCXX_PSEUDO_VISIBILITY # define _GLIBCXX_PSEUDO_VISIBILITY(V) #endif #if _GLIBCXX_HAVE_ATTRIBUTE_VISIBILITY # define _GLIBCXX_VISIBILITY(V) __attribute__ ((__visibility__ (#V))) #else # define _GLIBCXX_VISIBILITY(V) _GLIBCXX_PSEUDO_VISIBILITY(V) #endif
如果_GLIBCXX_HAVE_ATTRIBUTE_VISIBILITY
,那么设置可见性,不然他就是空的.
如果宏_GLIBCXX_INLINE_VERSION
为真,这个_GLIBCXX_BEGIN_NAMESPACE_VERSION
将会套上一个版本号为名的命名空间,否则啥也不干.
内部类
_State
1 2 3 4 5 6 7 8 9 10 11 12 class thread {public : struct _State { virtual ~_State(); virtual void _M_run() = 0 ; }; using _State_ptr = unique_ptr<_State>;
一个抽象基类_State
用于封装可执行对象,然后定义一个类型_State_ptr
,这是一个用来指向_State
的智能指针.
线程id
1 typedef __gthread_t native_handle_type;
__gthread_t
在gthr.h
中被定义为pthread_t
,这是linux
的线程id的数据类型,同一进程内唯一.
这个pthread_t
和pid_t
有区别.
pid_t
是标志进程的另一套机制.
众所周知,linux
的线程是一个特殊的进程,pid_t
是这个进程的实际的id.
为了将这些线程伪装成同一个进程,当我们使用getpid()
时,这些线程会返回主线程的进程id.
使用gettid()
可以获取他们真正的进程id.
这个pid_t
是全局唯一的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class id { native_handle_type _M_thread; public : id () noexcept : _M_thread() { } explicit id (native_handle_type __id) : _M_thread(__id) { } private : friend class thread ; friend class hash <thread::id>; friend bool operator ==(thread::id __x, thread::id __y) noexcept ; friend bool operator <(thread::id __x, thread::id __y) noexcept ; template <class _CharT , class _Traits >friend basic_ostream<_CharT, _Traits>&operator <<(basic_ostream<_CharT, _Traits>& __out, thread::id __id); }; private : id _M_id;
这个内部类有一个pthread_t
成员,他定义了一些友元函数,以便外部函数使用和比较他的pthread_id
.
并且重载了operator<<
以便打印线程id.
1 2 3 4 5 template <typename _Tp> using __not_same = __not_<is_same<__remove_cvref_t <_Tp>, thread>>;
__remove_cvref_t
可以移除_Tp
类型的const
,volatitle
和引用属性.
然后对比去除这些属性的_Tp
类型和thread
类型,判断是否相同.
如果相同,那么__not_same
类型的value
成员会是false
,否则为true
.
生命周期相关
构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public : thread () noexcept = default ; template <typename _Callable, typename ... _Args, typename = _Require<__not_same<_Callable>>> explicit thread (_Callable&& __f, _Args&&... __args) { static_assert ( __is_invocable<typename decay<_Callable>::type, typename decay<_Args>::type...>::value, "std::thread arguments must be invocable after conversion to rvalues" );#ifdef GTHR_ACTIVE_PROXY auto __depend = reinterpret_cast <void (*)()>(&pthread_create);#else auto __depend = nullptr ;#endif _M_start_thread(_S_make_state( __make_invoker(std::forward<_Callable>(__f), std::forward<_Args>(__args)...)), __depend); }
_Require
的实现如下
1 2 3 4 5 6 7 8 9 10 11 template <bool , typename _Tp = void > struct enable_if { };template <typename _Tp> struct enable_if <true , _Tp> { typedef _Tp type; };template <typename ... _Cond> using _Require = typename enable_if<__and_<_Cond...>::value>::type;
_Require
的作用:
如果可变模板参数_Cond
进行and
运算之后的::value
为真(也就是这里的_Callable
的类型和thread
不同),那么_Require
类型为合法类型(是一个void
).
否则他会是一个错误的类型,编译将会报错.
1 2 3 4 static_assert ( __is_invocable<typename decay<_Callable>::type, typename decay<_Args>::type...>::value, "std::thread arguments must be invocable after conversion to rvalues" );
判断_Callable
是否可以执行,根据判断结果,__is_invocable
最终会继承true_type
或false_type
.
对这个__is_invocable
取value
,可以得知是否可以调用.
如果不能调用就会报错std::thread arguments must be invocable after conversion to rvalues
.
__make_invoker
1 2 3 4 5 6 7 8 9 10 11 #ifdef GTHR_ACTIVE_PROXY auto __depend = reinterpret_cast <void (*)()>(&pthread_create);#else auto __depend = nullptr ;#endif _M_start_thread(_S_make_state( __make_invoker(std::forward<_Callable>(__f), std::forward<_Args>(__args)...)), __depend); }
__make_invoker
会返回一个_Invoker
对象,函数的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 template <typename ... _Tp> using __decayed_tuple = tuple<typename decay<_Tp>::type...>; template <typename _Callable, typename ... _Args> static _Invoker<__decayed_tuple<_Callable, _Args...>> __make_invoker(_Callable&& __callable, _Args&&... __args) {return { __decayed_tuple<_Callable, _Args...>{ std::forward<_Callable>(__callable), std::forward<_Args>(__args)... } }; } };
__decayed_tuple<typename... _Tp>{__args...}
可以将大括号内的元素包装成一个元组,元祖内每个元素的类型由尖括号内的模板参数决定.
return
会把这个元祖转换为一个_Invoker
,这是一个仿函数.后面会提到.
这里是第一次 移动,这个_Invoker
位于栈上,构造_Invoker
的时候参数被移动到_Invoker
的成员_M_t
.
_S_make_state
1 2 3 4 5 6 7 template <typename _Callable> static _State_ptr _S_make_state(_Callable&& __f) {using _Impl = _State_impl<_Callable>;return _State_ptr{new _Impl{std::forward<_Callable>(__f)}}; }
这个函数接收一个可执行对象__f
把它包装成一个_State_impl
对象,并返回指向这个对象的智能指针.
这个_State_impl
后面会提到.
这个_State_ptr
是前面定义的指向内部类_State
的智能指针.
这里是第二次 移动,这里把可执行对象_Invoker
移动到堆内存,并且用_State_ptr
指向他.
这个_State_ptr
指向的内存将会在线程结束的时候析构,析构时才会释放堆上的这块空间.
_M_start_thread
1 2 3 4 5 6 7 8 9 10 void thread::_M_start_thread(_State_ptr state, void (*)()) { const int err = __gthread_create(&_M_id._M_thread, &execute_native_thread_routine, state.get ()); if (err) __throw_system_error(err); state.release (); }
同state
指针指向的内容创建一个线程,然后释放state
.
创建之后这个线程的线程id会保存在内部对象_M_id
的_M_thread
成员中.
这里state
只是release
而没有释放内存.
execute_native_thread_routine
execute_native_thread_routine
是执行的关键,他被系统调用,并传入上文中state.get()
获得的指针
1 2 3 4 5 6 7 static void *execute_native_thread_routine (void * __p) { thread::_State_ptr __t { static_cast <thread::_State*>(__p) }; __t ->_M_run(); return nullptr ; }
一顿转换之后获得了指向可执行对象的智能指针__t
.
__t->_M_tun()
将会执行_Invoker
的operator()()
.
第三次 移动发生在这里.
把堆上的内容移动到栈上,传给std::__invoke
,从而执行.
析构函数
1 2 3 4 5 ~thread () { if (joinable ()) std::terminate (); }
joinable()
会新建一个id
,和当前的_M_id
对比,如果相同,那么说明这个线程没有传入一个可执行对象,或者已经被join
了.
否则执行std::terminate
结束进程.详情见这里 .
拷贝构造函数和移动构造函数
1 2 3 4 thread (const thread&) = delete ;thread (thread&& __t ) noexcept { swap (__t ); }
拷贝赋值函数和移动赋值函数
1 2 3 4 5 6 7 8 9 10 11 12 13 thread& operator =(const thread&) = delete ; thread& operator =(thread&& __t ) noexcept { if (joinable ()) std::terminate (); swap (__t ); return *this ; } void swap (thread& __t ) noexcept { std::swap (_M_id, __t ._M_id); }
移动赋值之前需要先判断是否terminate
,移动__t
的内容到当前对象.
基本操作
joinable
1 2 3 bool joinable () const noexcept { return !(_M_id == id ()); }
新建一个id
,和当前的_M_id
对比,如果相同,那么说明这个线程没有传入一个可执行对象,或者已经被join
了.
join
实现在thread.cc
文件中
1 2 3 4 5 6 7 8 9 10 void thread::join () { int __e = EINVAL; if (_M_id != id ()) __e = __gthread_join(_M_id._M_thread, 0 ); if (__e) __throw_system_error(__e); _M_id = id (); }
如果当前是joinable
的,就调用__gthread_join
.
然后_M_id
恢复为id()
.
__gthread_join
定义如下:
1 2 3 4 5 6 7 # define __gthrw_(name) __gthrw_ ## name static inline int __gthread_join (__gthread_t __threadid, void **__value_ptr) { return __gthrw_(pthread_join) (__threadid, __value_ptr); }
调用__gthrw_pthread_join
,传入_M_id._M_thread
和一个0
.
我目前没有找到他的实现,猜测应该是pthread_join
封装,以后找到了再补充.
detach
他的实现如下
1 2 3 4 5 6 7 8 9 10 void thread::detach () { int __e = EINVAL; if (_M_id != id ()) __e = __gthread_detach(_M_id._M_thread); if (__e) __throw_system_error(__e); _M_id = id (); }
也是和join
只能找到这里
1 2 3 4 __gthread_detach (__gthread_t __threadid) { return __gthrw_(pthread_detach) (__threadid); }
获取id
1 2 3 4 5 6 7 8 9 thread::id get_id () const noexcept { return _M_id; }native_handle_type native_handle () { return _M_id._M_thread; }
返回实现支持的并发线程数量
1 2 3 static unsigned int hardware_concurrency () noexcept ;
实现如下
1 2 3 4 5 6 7 8 unsigned int thread::hardware_concurrency () noexcept { int __n = _GLIBCXX_NPROCS; if (__n < 0 ) __n = 0 ; return __n; }
内部类 II
_State_impl
1 2 3 4 5 6 7 8 9 10 11 12 private : template <typename _Callable> struct _State_impl : public _State { _Callable _M_func; _State_impl(_Callable&& __f) : _M_func(std::forward<_Callable>(__f)) { }void _M_run() { _M_func(); } };
这个类继承了_State
并且有一个可执行对象作为成员.
将_M_run()
实现为执行这个可执行对象.
在构造的位置,他把接收到的接收到的_Invoker
,当作_M_func
.
一些内部函数
1 2 3 4 5 6 7 8 9 10 void _M_start_thread(_State_ptr, void (*)()); template <typename _Callable> static _State_ptr _S_make_state(_Callable&& __f) {using _Impl = _State_impl<_Callable>;return _State_ptr{new _Impl{std::forward<_Callable>(__f)}}; }
前面构造函数已经讲过.
内部类 III
_Impl_base
1 2 3 4 5 6 7 8 9 10 #if _GLIBCXX_THREAD_ABI_COMPAT public : struct _Impl_base ; typedef shared_ptr<_Impl_base> __shared_base_type; struct _Impl_base { __shared_base_type _M_this_ptr; virtual ~_Impl_base() = default ; virtual void _M_run() = 0 ; };
这个_Impl_base
持有一个指向_Impl_base
的智能指针.目前意义不明,后面将会提到.
1 2 3 4 5 6 7 private : void _M_start_thread(__shared_base_type, void (*)()); void _M_start_thread(__shared_base_type);#endif
实现如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #if _GLIBCXX_THREAD_ABI_COMPAT void thread::_M_start_thread(__shared_base_type __b) { if (!__gthread_active_p())#if __cpp_exceptions throw system_error (make_error_code (errc::operation_not_permitted), "Enable multithreading to use std::thread" );#else __throw_system_error(int (errc::operation_not_permitted));#endif _M_start_thread(std::move (__b), nullptr ); } void thread::_M_start_thread(__shared_base_type __b, void (*)()) { auto ptr = __b.get (); ptr->_M_this_ptr = std::move (__b); int __e = __gthread_create(&_M_id._M_thread, &execute_native_thread_routine_compat, ptr); if (__e) { ptr->_M_this_ptr.reset (); __throw_system_error(__e); } }#endif
他和前面提到的thread::_M_start_thread(_State_ptr state, void (*)())
不同.
目前没看出来哪里会用到这个内部类,日后补充.
_Invoker
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private : template <typename _Tuple> struct _Invoker { _Tuple _M_t;template <typename > struct __result ;template <typename _Fn, typename ... _Args> struct __result <tuple<_Fn, _Args...>> : __invoke_result<_Fn, _Args...> { };template <size_t ... _Ind> typename __result<_Tuple>::type _M_invoke(_Index_tuple<_Ind...>) { return std::__invoke(std::get <_Ind>(std::move (_M_t))...); }typename __result<_Tuple>::type operator () () { using _Indices = typename _Build_index_tuple<tuple_size<_Tuple>::value>::__type; return _M_invoke(_Indices()); } };
_M_invoke
这个函数把_M_t
中的每个元素取出,传给std::__invoke
以执行.
然后返回__result<_Tuple>::type
类型的返回值.
这里的具体实现日后补充.
__make_invoker
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 template <typename ... _Tp> using __decayed_tuple = tuple<typename decay<_Tp>::type...>; public : template <typename _Callable, typename ... _Args> static _Invoker<__decayed_tuple<_Callable, _Args...>> __make_invoker(_Callable&& __callable, _Args&&... __args) {return { __decayed_tuple<_Callable, _Args...>{ std::forward<_Callable>(__callable), std::forward<_Args>(__args)... } }; } };
上面构造函数处已经介绍.
线程比较操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 inline void swap (thread& __x, thread& __y) noexcept { __x.swap (__y); }inline bool operator ==(thread::id __x, thread::id __y) noexcept { return __x._M_thread == __y._M_thread; }inline bool operator !=(thread::id __x, thread::id __y) noexcept { return !(__x == __y); }inline bool operator <(thread::id __x, thread::id __y) noexcept { return __x._M_thread < __y._M_thread; }inline bool operator <=(thread::id __x, thread::id __y) noexcept { return !(__y < __x); }inline bool operator >(thread::id __x, thread::id __y) noexcept { return __y < __x; }inline bool operator >=(thread::id __x, thread::id __y) noexcept { return !(__x < __y); }
纯粹是在数值上对比线程id.
哈希
1 2 3 4 5 6 7 8 9 10 template <> struct hash <thread::id> : public __hash_base<size_t , thread::id> { size_t operator () (const thread::id& __id) const noexcept { return std::_Hash_impl::hash (__id._M_thread); } };
关键的函数_Hash_impl::hash
定义于bits/functional_hash.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct _Hash_impl { static size_t hash (const void * __ptr, size_t __clength, size_t __seed = static_cast <size_t >(0xc70f6907 UL)) { return _Hash_bytes(__ptr, __clength, __seed); } template <typename _Tp> static size_t hash (const _Tp& __val) { return hash (&__val, sizeof (__val)); } template <typename _Tp> static size_t __hash_combine(const _Tp& __val, size_t __hash) { return hash (&__val, sizeof (__val), __hash); } };
他最终调用了_Hash_bytes
.定义于hash_bytes.cc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 _Hash_bytes(const void * ptr, size_t len, size_t seed) { static const size_t mul = (((size_t ) 0xc6a4a793 UL) << 32UL ) + (size_t ) 0x5bd1e995 UL; const char * const buf = static_cast <const char *>(ptr); const int len_aligned = len & ~0x7 ; const char * const end = buf + len_aligned; size_t hash = seed ^ (len * mul); for (const char * p = buf; p != end; p += 8 ) { const size_t data = shift_mix (unaligned_load (p) * mul) * mul; hash ^= data; hash *= mul; } if ((len & 0x7 ) != 0 ) { const size_t data = load_bytes (end, len & 0x7 ); hash ^= data; hash *= mul; } hash = shift_mix (hash) * mul; hash = shift_mix (hash); return hash; }
打印线程
1 2 3 4 5 6 7 8 9 template <class _CharT , class _Traits > inline basic_ostream<_CharT, _Traits>& operator <<(basic_ostream<_CharT, _Traits>& __out, thread::id __id) { if (__id == thread::id ())return __out << "thread::id of a non-executing thread" ; else return __out << __id._M_thread; }
this thread
以下是一些获取id,睡眠相关的函数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 namespace this_thread { inline thread::id get_id () noexcept {#ifdef __GLIBC__ if (!__gthread_active_p()) return thread::id (1 );#endif return thread::id (__gthread_self()); } inline void yield () noexcept {#ifdef _GLIBCXX_USE_SCHED_YIELD __gthread_yield();#endif } void __sleep_for(chrono::seconds, chrono::nanoseconds); template <typename _Rep, typename _Period> inline void sleep_for (const chrono::duration<_Rep, _Period>& __rtime) { if (__rtime <= __rtime.zero ()) return ; auto __s = chrono::duration_cast <chrono::seconds>(__rtime); auto __ns = chrono::duration_cast <chrono::nanoseconds>(__rtime - __s);#ifdef _GLIBCXX_USE_NANOSLEEP __gthread_time_t __ts = { static_cast <std::time_t >(__s.count ()), static_cast <long >(__ns.count ()) }; while (::nanosleep (&__ts, &__ts) == -1 && errno == EINTR) { }#else __sleep_for(__s, __ns);#endif } template <typename _Clock, typename _Duration> inline void sleep_until (const chrono::time_point<_Clock, _Duration>& __atime) { auto __now = _Clock::now (); if (_Clock::is_steady) { if (__now < __atime) sleep_for (__atime - __now); return ; } while (__now < __atime) { sleep_for (__atime - __now); __now = _Clock::now (); } } }