1. async 与promise,future
1.1 async以及获取时间
async函数如下所示
template<typename _Fn, typename... _Args>
future<__async_result_of<_Fn, _Args...>>
async(launch __policy, _Fn&& __fn, _Args&&... __args)
{
std::shared_ptr<__future_base::_State_base> __state;
if ((__policy & launch::async) == launch::async)
{
__try
{
__state = __future_base::_S_make_async_state(
std::thread::__make_invoker(std::forward<_Fn>(__fn),
std::forward<_Args>(__args)...)
);
}
#if __cpp_exceptions
catch(const system_error& __e)
{
if (__e.code() != errc::resource_unavailable_try_again
|| (__policy & launch::deferred) != launch::deferred)
throw;
}
#endif
}
if (!__state)
{
__state = __future_base::_S_make_deferred_state(
std::thread::__make_invoker(std::forward<_Fn>(__fn),
std::forward<_Args>(__args)...));
}
return future<__async_result_of<_Fn, _Args...>>(__state);
}
std::async的操作,其实相当于封装了std::promise、std::packaged_task加上std::thread。
返回值为future对象,第一个参数为policy,第二个为新起线程执行的函数,第三个参数为函数参数,其作用为另起一个线程,执行函数,并且返回future,实现和主线程的同步。主线程中执行future::get,完成线程同步。
launch有如下三种情况:
- std::launch::async 传递的可调用对象异步执行;
- std::launch::deferred 传递的可调用对象同步执行;当调用get时,async才执行
- 混合情况
返回类型:
future_status有三种状态:
- deferred:异步操作还没开始
- ready:异步操作已经完成
- timeout:异步操作超时
获取future结果有三种方式:get、wait、wait_for,其中get等待异步操作结束并返回结果,wait只是等待异步操作完成,没有返回值,wait_for是超时等待返回结果。
注意:future::wait_for时间到后,返回的是timeout状态,当async里is_prime函数执行结束,为ready状态
#include <future>
#include <iostream>
#include <time.h>
std::thread::id fun(int i)
{
std::cout<<"go!"<<std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout<<"hello"<<i<<std::endl;
std::thread::id tid = std::this_thread::get_id();
std::cout<<tid<<std::endl;
return tid;
}
class A
{
public:
private:
int b=2;
};
int main()
{
A a;
int* b = reinterpret_cast<int*>(&a);
std::cout<<*b<<std::endl;
time_t time1 = time(NULL);
auto now1 = std::chrono::system_clock::now();
std::future<std::thread::id> fu = std::async(std::launch::async,fun,2018); //注意这里,std::launch::deferred为get时刻开始运行
std::cout<<"hello"<<std::endl;
std::chrono::milliseconds spans(5000);
std::this_thread::sleep_for(spans);
// while(fu.wait_for(spans)!=std::future_status::ready)
// std::cout<<" . ";
std::cout << std::endl;
std::thread::id i = fu.get();
pthread_t* tid = reinterpret_cast<pthread_t*>(&i);
std::cout<<*tid<<std::endl;
time_t time2 = time(NULL);
auto now2 = std::chrono::system_clock::now();
auto du = std::chrono::duration_cast<std::chrono::milliseconds>(now2-now1);
std::cout<<static_cast<float>(du.count())/static_cast<float>(1000)<<std::endl;
std::cout<<time2-time1<<std::endl;
return 0;
}
1.2 packaged_task
观察packaged_task源码,其与future一致,均不能拷贝
template<typename _Res, typename... _ArgTypes>
class packaged_task<_Res(_ArgTypes...)>
{
typedef __future_base::_Task_state_base<_Res(_ArgTypes...)> _State_type;
shared_ptr<_State_type> _M_state;
public:
// Construction and destruction
packaged_task() noexcept { }
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2095. missing constructors needed for uses-allocator construction
template<typename _Allocator>
packaged_task(allocator_arg_t, const _Allocator& __a) noexcept
{ }
template<typename _Fn, typename = typename
__constrain_pkgdtask<packaged_task, _Fn>::__type>
explicit
packaged_task(_Fn&& __fn)
: packaged_task(allocator_arg, std::allocator<int>(),
std::forward<_Fn>(__fn))
{ }
// No copy
packaged_task(const packaged_task&) = delete;
packaged_task& operator=(const packaged_task&) = delete;
// Result retrieval
future<_Res>
get_future()
{ return future<_Res>(_M_state); }
packaged_task传入函数指针等,然后get_future,再通过ref传入thread。在主线程中调用get
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
int fun(int i)
{
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout<<"hello"<<i<<std::endl;
return i;
}
int main()
{
std::packaged_task<int(int)> pt(fun);
std::future<int> fu = pt.get_future();
std::thread th(std::move(pt),22);
std::cout<<"sha???"<<std::endl;
int i = fu.get();
std::cout<<"world"<<i<<std::endl;
th.join();
return 0;
}
1.3 promise与future
1.3.1 future源码
future函数源码如下:
/// Primary template for future.
template<typename _Res>
class future : public __basic_future<_Res>
{
friend class promise<_Res>;
template<typename> friend class packaged_task;
template<typename _Fn, typename... _Args>
friend future<__async_result_of<_Fn, _Args...>>
async(launch, _Fn&&, _Args&&...);
typedef __basic_future<_Res> _Base_type;
typedef typename _Base_type::__state_type __state_type;
explicit
future(const __state_type& __state) : _Base_type(__state) { }
public:
constexpr future() noexcept : _Base_type() { }
/// Move constructor
future(future&& __uf) noexcept : _Base_type(std::move(__uf)) { }
// Disable copying
future(const future&) = delete;
future& operator=(const future&) = delete;
future& operator=(future&& __fut) noexcept
{
future(std::move(__fut))._M_swap(*this);
return *this;
}
/// Retrieving the value
_Res
get()
{
typename _Base_type::_Reset __reset(*this);
return std::move(this->_M_get_result()._M_value());
}
shared_future<_Res> share() noexcept;
};
注意future是不能拷贝的,所以要move或者ref
1.3.2 promise
std::promise的作用就是提供一个不同线程之间的数据同步机制,它可以存储一个某种类型的值,并将其传递给对应的future, 即使这个future不在同一个线程中也可以安全的访问到这个值。一个promise只能被获取到一次。
- std::promise::get_future:返回一个与promise共享状态相关联的future对象
- std::promise::set_value:设置共享状态的值,此后promise共享状态标识变为ready
- std::promise::set_exception:为promise设置异常,此后promise的共享状态标识变为ready
- std::promise::set_value_at_thread_exit:设置共享状态的值,但是不将共享状态的标志设置为 ready,当线程退出时该 promise 对象会自动设置为 ready(注意:该线程已设置promise的值,如果在线程结束之后有其他修改共享状态值的操作,会抛出future_error(promise_already_satisfied)异常)
- std::promise::swap:交换 promise 的共享状态
#include <future>
#include <iostream>
#include <thread>
#include <chrono>
void fun(std::future<int> &f)
{
int x = f.get();
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout<<"hello"<<x<<std::endl;
}
int main()
{
std::promise<int> pro;
std::future<int> fu = pro.get_future();
std::thread th1(fun,std::ref(fu));
pro.set_value(5);
th1.join();
return 0;
}
2. async的阻塞情况分析
之前项目中使用了async,但是遇到了坑,async发生了阻塞,后来改用thread解决了。查询之后发现:
future对象析构的时候,可能发生阻塞:
- 共享状态是通过std::async创建
- 共享状态还不是ready状态
- 被析构的future对象是共享状态的最后一个引用
如果直接使用std::async则,很有可能发生阻塞,因为其产生了临时变量future,析构时发现非ready,则产生阻塞
当共享状态是ready时候,解除阻塞,完成析构
为了避免上述问题,可以延长async的返回值的生命周期,比如:将future类型对象变为返回值等
future< int> fun()
{
auto fut1 = std::async…;
return fun1;
}
2.1 例子分析1
分析上面代码,按照设计目的,This shold show before work done 2!?应该是在work done 2!之前执行,但是这里async发生了阻塞,等sleep_for完成后,future_status状态变成ready,后future析构完成,才能执行输出
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
std::future<int> funasync()
{
auto fut1 = std::async(std::launch::async, [] { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); std::cout << "work done 1!\n";
return 1;});
std::cout << "Work done - implicit join on fut1 associated thread just ended\n\n";
std::cout << "Test 2 start" << std::endl;
std::async(std::launch::async, [] { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); std::cout << "work done 2!" << std::endl; });
std::cout << "This shold show before work done 2!?" << std::endl;
return fut1;
}
int funpack()
{
std::thread th([](){
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
std::cout << "funpack start" << std::endl;
return 1;
});
th.detach();
}
int main() {
using namespace std::literals;
funasync();
std::packaged_task<int(void)> pt(funpack);
std::future<int> fu = pt.get_future();
//std::thread ts(std::move(pt));
pt();
std::future_status st = fu.wait_for(std::chrono::seconds(6));//wait_for可设置超时时间,如果在超时时间之内任务完成
//,则返回std::future_status::ready状态;如果在超时时间之内任务尚未完成,则返回std::future_status::timeout状态
if (st == std::future_status::ready)
{
std::cout << "~~~~" << std::endl;
}
else if (st == std::future_status::timeout)
{
std::cout << "·····" << std::endl;
}
else if (st == std::future_status::deferred)
{
std::cout << "11111" << std::endl;
}
int a = fu.get();
std::cout << a << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
std::cout << "end main" << std::endl;
return 0;
}
结果如下所示:
xili271948@er04180p:/data/lxz/tcp$ ./stdasync
Work done - implicit join on fut1 associated thread just ended
Test 2 start
work done 1!
work done 2!
This shold show before work done 2!?
~~~~
0
funpack start
end main
xili271948@er04180p:/data/lxz/tcp$
2.2 例子分析2
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
std::future<int> funasync()
{
auto fut1 = std::async(std::launch::async, [] { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); std::cout << "work done 1!\n";
return 1;});
std::cout << "Work done - implicit join on fut1 associated thread just ended\n\n";
std::cout << "Test 2 start" << std::endl;
{auto fut2 = std::async(std::launch::async, [] { std::this_thread::sleep_for(std::chrono::milliseconds(5000)); std::cout << "work done 2!" << std::endl; });
std::cout << "async2 " << std::endl;
}
std::cout << "This shold show before work done 2!?" << std::endl;
return fut1;
}
int funpack()
{
std::thread th([](){
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
std::cout << "funpack start" << std::endl;
return 1;
});
th.detach();
}
class Date
{
private:
int year_, month_, date_;
public:
Date(int year,int month,int date):date_(date),month_(month),year_(year){}
friend void operator<<(std::ostream&,Date&);
};
void operator<<(std::ostream& out,Date& data)
{
out<<data.date_<<data.month_<<data.year_<<std::endl;
}
int main() {
Date mydata(2020,12,21);
std::ostream& out = std::cout;
out<<mydata;
out<<"out"<<std::endl;
// using namespace std::literals;
funasync();
std::packaged_task<int(void)> pt(funpack);
std::future<int> fu = pt.get_future();
//std::thread ts(std::move(pt));
pt();
std::future_status st = fu.wait_for(std::chrono::seconds(1));//wait_for可设置超时时间,如果在超时时间之内任务完成
//,则返回std::future_status::ready状态;如果在超时时间之内任务尚未完成,则返回std::future_status::timeout状态
if (st == std::future_status::ready)
{
std::cout << "~~~~" << std::endl;
}
else if (st == std::future_status::timeout)
{
std::cout << "·····" << std::endl;
}
else if (st == std::future_status::deferred)
{
std::cout << "11111" << std::endl;
}
int a = fu.get();
std::cout << a << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
std::cout << "end main" << std::endl;
return 0;
}
结果如下所示:
xili271948@er04180p:/data/lxz/tcp$ ./stdasync
21122020
out
Work done - implicit join on fut1 associated thread just ended
Test 2 start
async2
work done 1!
work done 2!
This shold show before work done 2!?
late
~~~~
0
funpack start
end main