熟悉一下 C++20 引入的 stop_xxx 即 <stop_token> 头文件提供的 Thread cancellation 的支持。
熟悉多线程编程了,一般 demux 程序(比如基于 epoll/select/poll 的大部分时间的“死”循环)就是一个状态机。过程中我们如果希望调控他的话,就要使用变量(作为状态机的输入),达到一个检查状态的时候,程序(一般是我们写的死循环)就会响应我们的输入,给出输出。或者说是一种信号机制吧,Nginx 的信号控制进程的停止、reload 升级也是通过信号机制,不是实际实现一般就是通过一些线程安全的变量吧。比如用 atomic,然后一般也要加上锁。
C++ 20 提出了 std::stop_token 作为 ?a thread-safe "view" of the associated stop-state. 这个东西要配合一个 stop_source 的东西使用
以下内容基于 MSVC++ 14 (就 jthread 上来说大家的实现都差不多)。首先是对于 jthread 的说明,jthread 是 (would be)joined 的 thread 的意思吧。然后对于 stop_token 的支持是如果你的线程函数的第一个参数是 stop_token 类型,就会传入他自己的 stop_source.
看一个例子:
#include <iostream>
#include <thread>
using namespace std;
using namespace std::literals::chrono_literals;
void test(stop_token t) {
while (t.stop_requested() == false) {
std::cout << "t is: " << t.stop_requested() << endl;
}
std::cout << "stop! " << t.stop_requested() << endl;
}
int main(void) {
jthread th(test);
this_thread::sleep_for(3s);
th.request_stop();
this_thread::sleep_for(3s);
std::cout << "Main end" << endl;
}
其执行结果是这样的:
首先 jthread 实现特殊参数控制用到的 type_traits 是 is_invocable_v<decay_t<_Fn>(防止忘记此处复习一下 decay 是用来 removes cv-qualifiers, and defines the resulting type as the member typedef type,即获取原本的样子,毕竟 is_invocable 这些用来万能引用的模板都会导致 T 有可能推导为 decay<T>&) 。当然,is_invocable 本身是 C++17 提供支持的,C++11 是没有的。
std::is_invocable, std::is_invocable_r, std::is_nothrow_invocable, std::is_nothrow_invocable_r - cppreference.com
template <class _Fn, class... _Args, enable_if_t<!is_same_v<remove_cvref_t<_Fn>, jthread>, int> = 0>
_NODISCARD_CTOR explicit jthread(_Fn&& _Fx, _Args&&... _Ax) {
if constexpr (is_invocable_v<decay_t<_Fn>, stop_token, decay_t<_Args>...>) {
_Impl._Start(_STD forward<_Fn>(_Fx), _Ssource.get_token(), _STD forward<_Args>(_Ax)...);
} else {
_Impl._Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
}
}
jthread 就绷到这里。然后我们来看怎么写自己的 stop_token。其实也很简单,我们抄 jthread 的源码就行了。
// 删减了无关部分。。
#if _HAS_CXX20
class jthread {
public:
using id = thread::id;
using native_handle_type = thread::native_handle_type;
jthread() noexcept : _Impl{}, _Ssource{nostopstate} {}
template <class _Fn, class... _Args, enable_if_t<!is_same_v<remove_cvref_t<_Fn>, jthread>, int> = 0>
_NODISCARD_CTOR explicit jthread(_Fn&& _Fx, _Args&&... _Ax);
~jthread() {
_Try_cancel_and_join();
}
jthread(const jthread&) = delete;
jthread(jthread&&) noexcept = default;
jthread& operator=(const jthread&) = delete;
jthread& operator=(jthread&& _Other) noexcept;
_NODISCARD bool joinable() const noexcept {
return _Impl.joinable();
}
_NODISCARD stop_source get_stop_source() noexcept {
return _Ssource;
}
_NODISCARD stop_token get_stop_token() const noexcept {
return _Ssource.get_token();
}
bool request_stop() noexcept {
return _Ssource.request_stop();
}
private:
void _Try_cancel_and_join() noexcept {
if (_Impl.joinable()) {
_Ssource.request_stop();
_Impl.join();
}
}
thread _Impl;
stop_source _Ssource;
};
好了,就这样。