c++20 std::jthread 源码简单赏析与应用

std::jthread

说明:

std::jthread 是 C++20 中引入的一个新特性,它是线程库中的一个类,专门用于处理 std::thread 与 std::stop_token 和 std::stop_source 之间的交互,以支持更优雅和安全的线程停止机制。

std::stop_source控制线程标记。相当于g_bQuitFlag = true;

std::stop_token线程函数内检测结束标记。相当于while(!g_bQuitFlag){...};

作用1.可控制的线程结束,防止人工封装的线程结束控制变量不是线程安全的。2.线程结束后不必手工调用join();

源码简单赏析:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\include\thread

下面代码可以复制出来浏览。

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) {
		if constexpr (is_invocable_v<decay_t<_Fn>, stop_token, decay_t<_Args>...>) 
		//线程函数第一个参数为stop_token,如果客户端std::jthread jt(work, stop_source.get_token());使用自己的stop_source展开相当于
		//is_invocable_v<decay_t<_Fn>, stop_token, stop_token>为false,执行下面else为不使用this->_Ssource,这里很微妙!
		//如果客户端std::jthread jt(work);不使用自己的stop_source展开相当于is_invocable_v<decay_t<_Fn>, stop_token>为true
		//也就是说这个if判断了三种情况,1.线程函数第一个不为stop_token 2.线程函数第一个参数为stop_token且客户端使用自己的stop_source 
		//3.线程函数第一个参数为stop_token且客户端使用不自己的stop_source
		{
			_Impl._Start(_STD forward<_Fn>(_Fx), _Ssource.get_token(), _STD forward<_Args>(_Ax)...);//按照使用this内置的stop_source::stop_token传给线程函数
		}
		else {
			_Impl._Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);//按照std::thread处理,不使joinable功能,那么this->request_stop();也无效。处于兼容性考虑吧
		}
	}

	~jthread() {
		_Try_cancel_and_join();//析构时可自动调用_Impl.joinable();_Ssource.request_stop();_Impl.join();
	}

	jthread(const jthread&) = delete;//不支持拷贝构造
	jthread(jthread&&) noexcept = default;//jthread依然是unique thread类型的
	jthread& operator=(const jthread&) = delete;//不支持赋值

	jthread& operator=(jthread&& _Other) noexcept {
		// note: the standard specifically disallows making self-move-assignment a no-op here
		// N4861 [thread.jthread.cons]/13
		// Effects: If joinable() is true, calls request_stop() and then join(). Assigns the state
		// of x to *this and sets x to a default constructed state.
		_Try_cancel_and_join();//移动赋值时,先结束掉本线程,再移动源线程到this
		_Impl = _STD move(_Other._Impl);
		_Ssource = _STD move(_Other._Ssource);
		return *this;
	}
	//剩下的简单函数封装
	void swap(jthread& _Other) noexcept {
		_Impl.swap(_Other._Impl);
		_Ssource.swap(_Other._Ssource);
	}

	_NODISCARD bool joinable() const noexcept {
		return _Impl.joinable();
	}

	void join() {
		_Impl.join();
	}

	void detach() {
		_Impl.detach();
	}

	_NODISCARD id get_id() const noexcept {
		return _Impl.get_id();
	}

	_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();
	}

	friend void swap(jthread& _Lhs, jthread& _Rhs) noexcept {
		_Lhs.swap(_Rhs);
	}

	_NODISCARD static unsigned int hardware_concurrency() noexcept {
		return thread::hardware_concurrency();
	}

private:
	void _Try_cancel_and_join() noexcept {
		if (_Impl.joinable()) {
			_Ssource.request_stop();
			_Impl.join();
		}
	}

	thread _Impl;//采用c++组合方式对std::thread进行薄薄的封装,是不是和std::queue差不多的方法?
	stop_source _Ssource;//使用std自己实现的原子操作类std::stop_source控制线程停止
};

应用:

根据源代码阅读衍生的几个例子。源代码和例子说的是一致的。

1.不使用jthread内置的stop_source相当于thread类,std::jthread::request_stop();将不起作用,方便于一个外置std::stop_source控制多个线程函数

#include <iostream>
#include <thread>
#include <chrono>

void work(std::stop_token stop_token) {
	while (!stop_token.stop_requested()) {
		std::cout << "Working...\n";
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
	std::cout << "Stopping...\n";
}

int main() {
	std::stop_source stop_source;
	std::jthread jt(work, stop_source.get_token());

	std::this_thread::sleep_for(std::chrono::seconds(3));
	stop_source.request_stop();
	return 0;
}

2.使用jthread内置的stop_source :

#include <iostream>
#include <thread>
#include <chrono>

void work(std::stop_token stop_token) {
	while (!stop_token.stop_requested()) {
		std::cout << "Working...\n";
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
	std::cout << "Stopping...\n";
}

int main() {
	std::jthread jt(work);
	std::this_thread::sleep_for(std::chrono::seconds(3));
    //这两行都没必要人工调用的
	jt.request_stop();//
	jt.join();//
	return 0;
}

3.当线程函数参数没有参数stop_token时,使用起来std::jthread::request_stop();不起作用。相当于std::thread:

#include <iostream>
#include <thread>
#include <chrono>

void work() {
	while (1) {
		std::cout << "Working...\n";
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
	std::cout << "Stopping...\n";
}

int main() {
	std::jthread jt(work);
	std::this_thread::sleep_for(std::chrono::seconds(3));
	jt.request_stop();
	jt.join();
	return 0;
}
//输出:
//Working...
//Working...
//Working...
//Working...
//...

结论:

功能过于强大,灵活,坑也比较多。功能少,呆板,也没有什么坑。其实没有什么坑,只不过没看过开源库源码。官方文档std::jthread - cppreference.com

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值