@著作权归作者所有:来自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、使用示例:
1:int(*Fun)(int);
2:std::packaged_task<int(int)> task(Fun);
3:std::future<int> res = task.get_future();
4:task(3);
5:res.get();
4和5一般在异步线程中
注意:
变化一:
1://。。。
2:std::packaged_task<int(int)> task(std::bind(Fun,4));
3://。。。
4:task(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