TheRiver | blog

You have reached the world's edge, none but devils play past here

0%

C++ std::thread

linux 2.1和linux2.6在线程实现上是不同的。在Linux2.4中,LinuxThreads是用单独的进程实现每个线程的,这使得它很难与posix线程的行为匹配。在linux2.6中,对linux内核和线程库进行了很大的修改,采用了一个称为Native POSIX线程库(NPTL)的新线程实现。它支持单个进程中有多个线程的模型,也更容易支持posix线程的语义。

date author gcc kernel glibc
20220331 river gcc-11.2.0 linux-4.18.1 glibc-2.35

std::thread

datastruct

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class thread
{
// ...
using native_handle_type = __gthread_t;

/// thread::id
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 struct hash<id>;

friend bool
operator==(id __x, id __y) noexcept;

#if __cpp_lib_three_way_comparison
friend strong_ordering
operator<=>(id __x, id __y) noexcept;
#else
friend bool
operator<(id __x, id __y) noexcept;
#endif

template<class _CharT, class _Traits>
friend basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __out, id __id);
};
// member
id _M_id;

// ctor
public:
thread() noexcept = default;

#ifdef _GLIBCXX_HAS_GTHREADS
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
// Create a reference to pthread_create, not just the gthr weak symbol.
auto __depend = reinterpret_cast<void(*)()>(&pthread_create);
#else
auto __depend = nullptr;
#endif
using _Wrapper = _Call_wrapper<_Callable, _Args...>;
// Create a call wrapper with DECAY_COPY(__f) as its target object
// and DECAY_COPY(__args)... as its bound argument entities.
_M_start_thread(_State_ptr(new _State_impl<_Wrapper>(
std::forward<_Callable>(__f), std::forward<_Args>(__args)...)),
__depend);
}

// dtor
~thread()
{
if (joinable())
std::terminate();
}

// rule of four
thread(const thread&) = delete;

thread(thread&& __t) noexcept
{ swap(__t); }

thread& operator=(const thread&) = delete;

thread& operator=(thread&& __t) noexcept
{
if (joinable())
std::terminate();
swap(__t);
return *this;
}

thread::id::native_handle_type在linux-posix下就是typedef pthread_t __gthread_t;c++的thread封装了pthread_t类型。删掉了拷贝构造和拷贝赋值函数。移动构造和移动赋值用到了经典了copy and swap idiom

joinable

checks whether the thread is joinable, i.e. potentially running in parallel context (public member function)

1
2
3
4
5
6
7
8
9
10
11
12
13
bool
joinable() const noexcept
{ return !(_M_id == id()); }

inline bool
operator==(thread::id __x, thread::id __y) noexcept
{
// pthread_equal is undefined if either thread ID is not valid, so we
// can't safely use __gthread_equal on default-constructed values (nor
// the non-zero value returned by this_thread::get_id() for
// single-threaded programs using GNU libc). Assume EqualityComparable.
return __x._M_thread == __y._M_thread;
}
1
2
3
4
5
6
7
8
9
int pthread_equal(pthread_t t1, pthread_t t2);

/*
The pthread_equal() function shall return a non-zero value if t1
and t2 are equal; otherwise, zero shall be returned.

If either t1 or t2 are not valid thread IDs, the behavior is
undefined.
*/

linux3.2.0使用无符号长整形表示pthread_t,但在其他平台实现是不一样的。所以这里通过和默认值做比较来判断_M_id是否是有效的。之所以不用pthread_equal原因如上。

这里有点不好理解再补充下: thread的构造函数传入callable执行函数,内部会掉pthread_create,pthread_create会申请pthread_t的值,然后赋值给thread::id::_M_thread,所以只有pthread创建了线程std::thread才是joinable的。

join

waits for the thread to finish its execution (public member function)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  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();
}

static inline int
__gthread_join (__gthread_t __threadid, void **__value_ptr)
{
return __gthrw_(pthread_join) (__threadid, __value_ptr);
}

调用posix的pthread_join来实现,第2个参数传的0,不接收pthread_exit传出来的值。最后给_M_id赋初始值表示无效。

detach

permits the thread to execute independently from the thread handle (public member function)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
}

static inline int
__gthread_detach (__gthread_t __threadid)
{
return __gthrw_(pthread_detach) (__threadid);
}

调用posix的pthread_detach来实现。最后给_M_id赋初始值表示无效。

swap

swaps two thread objects (public member function)

1
2
3
4
5
6
7
void
swap(thread& __t) noexcept
{ std::swap(_M_id, __t._M_id); }

inline void
swap(thread& __x, thread& __y) noexcept
{ __x.swap(__y); }

实现copy and swap

hardware_concurrency

returns the number of concurrent threads supported by the implementation (public static member function)

1
2
3
4
5
6
7
8
9
10
11
12
// Returns a value that hints at the number of hardware thread contexts.
static unsigned int
hardware_concurrency() noexcept;

unsigned int
thread::hardware_concurrency() noexcept
{
int __n = _GLIBCXX_NPROCS;
if (__n < 0)
__n = 0;
return __n;
}

在多核系统中返回cpu核心的数量,当无法获取返回0.

get_id

returns the id of the thread (public member function)

1
2
3
id
get_id() const noexcept
{ return _M_id; }

返回thread::id,还有个this_thread::get_id()可以返回任意thread的thread::id

native_handle

returns the underlying implementation-defined thread handle (public member function)

1
2
3
native_handle_type
native_handle()
{ return _M_id._M_thread; }

返回原生的线程id数据结构,posix下是pthread_t

reference

https://en.cppreference.com/w/cpp/thread/thread

https://man7.org/linux/man-pages/man3/pthread_equal.3p.html

https://bewaremypower.github.io/2018/12/31/C-11%E4%B8%ADstd-thread%E5%92%8Cpthread%E6%B7%B7%E7%94%A8%E7%9A%84%E5%9D%91/

----------- ending -----------