基于多线程并发-STL之多线程进阶-async、future、promise

@著作权归作者所有:来自CSDN博客作者大胡子的艾娃的原创作品,如需转载,请注明出处https://blog.csdn.net/qq_43148810,否则将追究法律责任。
如有错误的地方欢迎指正,谢谢!

一、async创建异步线程

async模板函数的两个版本源码(已去除这里不关注的部分)

enum class launch {
	async = 0x1,
	deferred = 0x2
	};

//返回值为std::future类型,?表省略,
template<class _Fty,class... _ArgTypes>
std::future<?> async(launch _Policy, _Fty&& _Fnarg, _ArgTypes&&... _Args)	
{
//此处省略多行代码
_Get_associated_state(_Policy,?)
};

//launch第一参数不传值,默认为launch::async | launch::deferred
template<class _Fty,class... _ArgTypes>
std::future<?> async(_Fty&& _Fnarg, _ArgTypes&&... _Args)
{
//调用的为上一版本	
return (std:: async(launch::async | launch::deferred,
		std::forward<_Fty>(_Fnarg),
		std::forward<_ArgTypes>(_Args)...
		));
}

源码说明:
1)第一模板参数_Fty为可调用对象;第二模板参数 _ArgTypes(Variadic Templates(…))为可调用对象_Fty的参数列。
2)有且仅有可调用对象形参为引用,例如:void fun(const T& arg1,T& arg2);,并且实参经过std::ref转换,例如:std::async(fun,std::ref(arg1),std::ref(arg2));,才实现真正的引用。否则编译报错或者未实现引用(主线程或子线程中至少做一次拷贝)。堆为线程共享空间,传值指针为经典的多线程维护堆空间问题.
3)当实例化第一参数为std::launch::deferred:延迟加载方式创建任务,但不创建线程,直到调用了future的get或者wait时才在主线程中执行该任务;若没调用,直到future对象出作用域消亡也不会执行。
4)当实例化第一参数为std::launch::async:在调用async就开始创建线程,并执行_Fty,future对象调用get、wait或该对象出作用域消亡时等待子线程结束。
5)如果用std::launch::async | std::launch::defferred,系统自行决定使用哪一种。

二、future模板类

主线程中获取子线程的值(future等待一次性事件)
future类源码(接口实现部分已去除)

// ENUM future_status
enum class future_status {	// names for timed wait function returns
	ready,					//子线程已经结束
	timeout,				//timeout是超时,等待的时间结束,而子线程仍然没有结束
	deferred				//子线程是延时执行的
	};
	
template<class _Ty>
	class _State_manager
	{
	//此处省略多行代码
	_NODISCARD bool valid() const noexcept;
	void wait() const;
	
	template<class _Rep,
		class _Per>
		future_status wait_for(const chrono::duration<_Rep, _Per>& _Rel_time) const;

	template<class _Clock,
		class _Dur>
		future_status wait_until(const chrono::time_point<_Clock, _Dur>& _Abs_time) const;
	//此处省略多行代码
	}
	
template<class _Ty>
	class future
		: public _State_manager<_Ty>
	{
	using _Mybase = _State_manager<_Ty>;
public:
	future() noexcept{}

	future(future&& _Other) noexcept
		: _Mybase(_STD move(_Other), true){}

	future& operator=(future&& _Right) noexcept;

	future(const _Mybase& _State, _Nil)
		: _Mybase(_State, true){}
		
	~future() noexcept{}

	_Ty get();

	_NODISCARD shared_future<_Ty> share() noexcept;

	future(const future&) = delete;
	future& operator=(const future&) = delete;
	};

类属性和接口说明
1)future还有future<_Ty&>偏特化和future全特化版本
2)get()等待线程运行完成,并获取线程的返回值(而thread无法实现)。调用get后该future无效,可再次调用valid方法,调用get、wait等方法运行出错。
3)设置超时时间,并获取线程的运行状态。

//future_status对应状态可见源码注释
std::future_status myStatus = algorithmFu.wait_for(std::chrono::milliseconds(0));
//wait_until接口用法基本一致

4)不允许拷贝构造、拷贝赋值,但有move语义
5)share()接口获取 shared_future
6)继承基类_State_manager效性检查valid接口,与get搭配使用
7)多个线程访问单个future对象而不进行额外的同步,get时会发生数据竞争
8)若主线程中无调用get(),则future对象析构(出作用域)时等待子线程执行完成,析构和wait()一样,只能确保线程结束,无法获取子线程返回值。而thread对象必须有且仅有调用一次join或者detach。
8)使用示例

#include <future>
int find_the_answer_to_ltuae(){ return 42; }
void do_other_stuff(){}
int main(){
    std::future<int> the_answer=std::async(find_the_answer_to_ltuae);
    do_other_stuff();
    int ret = the_answer.get();	
}

三、promise模板类

子线程中获取主线程的值(promise允诺)
promise类源码(接口实现部分已去除)

template<class _Ty>
	class promise
	{	// class that defines an asynchronous provider that holds a value
public:
	promise()
		: _MyPromise(new _Associated_state<_Ty>);

	template<class _Alloc>
		promise(allocator_arg_t, const _Alloc& _Al)
		: _MyPromise(_Make_associated_state<_Ty>(_Al));

	promise(promise&& _Other) noexcept
		: _MyPromise(_STD move(_Other._MyPromise));

	promise& operator=(promise&& _Other) noexcept;

	~promise() noexcept;

	void swap(promise& _Other) noexcept;
	
	_NODISCARD future<_Ty> get_future();

	void set_value(const _Ty& _Val);

	void set_value_at_thread_exit(const _Ty& _Val);

	void set_value(_Ty&& _Val);

	void set_value_at_thread_exit(_Ty&& _Val);

	void set_exception(exception_ptr _Exc);

	void set_exception_at_thread_exit(exception_ptr _Exc);

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

private:
	_Promise<_Ty> _MyPromise;
	};

类属性和接口说明
1、不允许拷贝构造、拷贝赋值,但有move语义
2、兑现允诺常用接口的有set_value(const _Ty& _Val)和set_value(_Ty&& _Val)、set_exception(std::exception_ptr _Exc)(期望储存一个异常)
3、同future一样,还有promise<_Ty&>偏特化和promise全特化版本
4、但promise是has a的结构而future是is a的结构,所以暂不介绍private成员_Promise<_Ty> _MyPromise
5、get_future接口返回一个异步关联的future对象,该对象调用get()获取允诺值,并且可以多次调用
6、使用示例

#include <future>
int algorithm(std::future<double>& Future){
	const double N = Future.get();			//阻塞等待Promise允诺
	return N * N;
}
int main(){
	std::promise<double> Promise;		//Promise允诺给Future设置一个double值
	std::future<double> Future = Promise.get_future();
	std::future<int> algorithmFu = std::async(algorithm, std::ref(Future));
	Promise.set_value(2.3);
	int ret = algorithmFu.get();		//主线程应先通过set_value实现对子线程的允诺,主线程才可以阻塞获取子线程返回值,顺序反了则死锁。
	return 0;
}

四、shared_future类

类似共享指针的future
shared_future类源码(接口实现部分已去除)

template<class _Ty>
	class shared_future
	: public _State_manager<_Ty>
	{	// class that defines a copyable asynchronous return object
		// that holds a value
	using _Mybase = _State_manager<_Ty>;

public:
	shared_future() noexcept
		{	// construct
		}

	shared_future(const shared_future& _Other) noexcept
		: _Mybase(_Other)
		{	// construct from shared_future object
		}

	shared_future& operator=(const shared_future& _Right) noexcept
		{	// assign from shared_future object
		_Mybase::operator=(_Right);
		return (*this);
		}

	shared_future(future<_Ty>&& _Other) noexcept
		: _Mybase(_STD forward<_Mybase>(_Other))
		{	// construct from rvalue future object
		}

	shared_future(shared_future&& _Other) noexcept
		: _Mybase(_STD move(_Other))
		{	// construct from rvalue shared_future object
		}

	shared_future& operator=(shared_future&& _Right) noexcept
		{	// assign from shared_future rvalue object
		_Mybase::operator=(_STD move(_Right));
		return (*this);
		}

	~shared_future() noexcept
		{	// destroy
		}

	const _Ty& get() const
		{	// block until ready then return the stored result or
			// throw the stored exception
		return (this->_Get_value());
		}
	};

类属性和接口说明
1、get()可被多次调用,返回储存的结果或者异常
2、同future一样,还有shared_future<_Ty&>偏特化和shared_future全特化版本
3、可以拷贝构造、拷贝赋值、并有move语义
4、shared_future实现为引用计数,类比shared_ptr
5、不能通过future对象作参数直接构造,只能通过future的右值(move得右值)传参构造和future对象调用share()接口创建
6、使用示例

#include <future>
#include <iostream>

int algorithm(std::shared_future<double> Future) {
	const double N = Future.get();
	const double N2 = Future.get();
	return N * N;
}
int main() {
	std::promise<double> Promise;
	std::shared_future<double> sharedFuture = Promise.get_future();	//future到shared_future隐式转换,类型转换构造完成
	
	std::future<int> algorithmFu = std::async(std::launch::async, algorithm, sharedFuture);
	std::future<int> algorithmFu2 = std::async(std::launch::async, algorithm, sharedFuture);
	std::future<int> algorithmFu3 = std::async(std::launch::async, algorithm, sharedFuture);
	//。。。
	Promise.set_value(2.3);				//向以上多个异步线程广播,并且是线程安全的
	std::cout << algorithmFu.get() << std::endl;
	std::cout << algorithmFu2.get() << std::endl;
	std::cout << algorithmFu3.get() << std::endl;
	return 0;
}

五、packaged_task

包装任何可调用 (Callable) 目标,包括函数、 lambda 表达式、 bind 表达式或其他函数对象,使得能异步调用它,其返回值或所抛异常被存储于能通过 std::future 对象访问的共享状态中(普通的可调用函数对象转换为异步执行的任务)
packaged_task源码(接口实现部分已去除)

template<class _Ret,
	class... _ArgTypes>
	class packaged_task<_Ret(_ArgTypes...)>
	{	// class that defines an asynchronous provider that returns the
		// result of a call to a function object
public:
	using _Ptype = typename _P_arg_type<_Ret>::type;
	using _MyPromiseType = _Promise<_Ptype>;
	using _MyStateManagerType = _State_manager<_Ptype>;
	using _MyStateType = _Packaged_state<_Ret(_ArgTypes...)>;

	packaged_task() noexcept
		: _MyPromise(0);
	template<class _Fty2,class = ???>			//???处作用参考文章“C++11标准库thread构造函数浅析”
		explicit packaged_task(_Fty2&& _Fnarg);
	packaged_task(packaged_task&& _Other) noexcept
		: _MyPromise(_STD move(_Other._MyPromise));
	packaged_task& operator=(packaged_task&& _Other) noexcept;

	~packaged_task() noexcept;

	void swap(packaged_task& _Other) noexcept;

	_NODISCARD bool valid() const noexcept;

	_NODISCARD future<_Ret> get_future();

	void operator()(_ArgTypes... _Args);

	void make_ready_at_thread_exit(_ArgTypes... _Args);

	void reset();

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

private:
	_MyPromiseType _MyPromise;
	};

类属性和接口说明
1、构造参数(除空构造和Move语义拷贝、赋值函数)只有一个可调用对象,可用bind
2、包含一个可调用对象,并且允许异步获取调用对象的结果即future
3、仿函数实现
4、不允许拷贝构造、拷贝赋值,但有move、swap、reset语义
5、has a的结构
6、使用示例:

1int(*Fun)(int);
2:std::packaged_task<int(int)> task(Fun);
3:std::future<int> res = task.get_future();
4task(3);
5:res.get();

4和5一般在异步线程中
注意:
变化一:

1//。。。
2:std::packaged_task<int(int)> task(std::bind(Fun,4));
3//。。。
4task(3);
5//。。。

第四行task的入参无效,第二行入参有效

变化二:

1//。。。
2:std::packaged_task<void(int)> task(Fun);
3:std::future<void> res = task.get_future();
4//。。。
5//。。。

则第五步get返回值为空,阻塞等待调用完成,返回值被丢弃

六、时钟

std::chrono::system_clock;	//非匀速时钟
std::chrono::steady_clock;	//匀速时钟,常用

1)时间点

std::chrono::steady_clock::time_point time_steady_P = std::chrono::steady_clock::now() + std::chrono::microseconds(10);

2)时间段

std::chrono::nanoseconds/microseconds/milliseconds/seconds/minutes/hours;

3)时间单位转换

milliseconds ms = milliseconds(2999);		//构造参数为
seconds s = duration_cast<seconds>(ms);		//小单位转大单位去尾法,反则补0

4)时间点运算得到时间段

std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
//do things
std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
std::chrono::milliseconds duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);

5)时间运算
ms.count()获取数字值,+,-等运算符重载(隐式转换:单位不同则大单位转成小单位),还有其他接口
7.时钟在多线程中的使用
1)std::thread

std::this_thread::sleep_for(ms);				//沉睡固定时间段
std::this_thread::sleep_until(time_steady_P);	//沉睡至定点时间

2)std::unique_lockstd::mutex

std::unique_lock<std::mutex> un_lock(my_mutex);
un_lock.try_lock_for(ms);						//沉睡固定时间段
un_lock.try_lock_until(ms);						//沉睡至定点时间

3)std::condition_variable/condition_variable_any

std::condition_variable my_cond;
my_cond.wait_for(un_lock,ms);					//沉睡固定时间段
my_cond.wait_until(un_lock,time_steady_P);		//沉睡至定点时间

4)std::future

std::promise<int> Promise;
std::future<int>Future = Promise.get_future();
Future.writ_for(ms);							//沉睡固定时间段
Future.writ_until(time_steady_P);				//沉睡固定时间段

如有错误或不足欢迎评论指出!创作不易,转载请注明出处。如有帮助,记得点赞关注哦(⊙o⊙)
更多内容请关注个人博客:https://blog.csdn.net/qq_43148810

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大胡子的艾娃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值