std::async() 、 std::package_task<> 、std::promise<> 、std::future<>、 std::shared_future<>
一、async()可以看做package_task<>和promise<>的封装
他们的作用都是获取线程的返回值。
我们前面所用的线程函数都是void类型的,但其实线程可以有返回值,当然我们可以用全局变量记录线程处理结果。
1、async()函数是最简单的使用方式
int threadFunc(int m)
{
cout << "threadFunc id:" << this_thread::get_id() << endl;
this_thread::sleep_for(chrono::seconds(5));
return ++m;
}
int main()
{
cout << "main id:" << this_thread::get_id() << endl;
int i = 1001;
future<int> result = async(threadFunc, i);
cout << result.get() << endl; //get()会等待线程结束并获取返回值,同wait()只等待线程结束,返回类型void
cout << "程序结束" << endl;
return 0;
}
async(threadFunc, i);其实是async(std::launch::async | std::launch::deferred, threadFunc, i);的简化形式。
这里引出async和thread的区别:
1、thread是创建线程,当资源紧张无法创建时程序会异常。 | 2、async是异步任务,当参数是std::launch::async时也会创建进程,但资源紧张系统无法创建线程时则不创建,此时的用法同参数std::launch::deferred。 | 3、std::launch::deferred延时调用,不创建进程,只是等到调用std::future<>::get()方法时才进行调用,但还是在同一个线程中。 | 4、std::launch::async | std::launch::deferred 同时使用,是创建线程还是延时调用,取决于系统。所以在我们想创建线程时要明确指定参数std::launch::async. | 创建一个异步任务函数,打印其线程id与主线程id相比较,就能看出std::launch::async和std::launch::deferred的区别。 |
二、promise<T>
示例如下,用可调用对象做promise的类型。
using T = function<int(int, int)>;
int calpuls(int a,int b)
{
return a + b;
}
void threadfunc1(promise<T> &pm)
{
cout << "threadfunc1 id:" << this_thread::get_id << endl;
this_thread::sleep_for(chrono::seconds(3));
pm.set_value(bind(&calpuls, std::placeholders::_1, std::placeholders::_2));
}
void thread2(future<T> &ft)
{
cout << "thread2 id:" << this_thread::get_id << endl;
auto f = ft.get();
cout << f(5, 8) << endl;
}
int main()
{
cout << "main id:" << this_thread::get_id() << endl;
promise<T> pmt; //定义promise
future<T> ftt = pmt.get_future(); //绑定future
thread t1(threadfunc1, std::ref(pmt)); //一定要用ref
thread t2(thread2, std::ref(ftt));
t1.join();
t2.join();
cout << "main end!" << endl;
return 0;
}
thread2要等待线程threadfunc1的执行,可见也是线程间同步。
三、packaged_task<T>
上文我们promise的类型是function<>包装的可调用对象,其实packaged_task<>主要的功能就是包装一个可调用对象,跟function<T>功能类似,并获取异步调用的结果。?
即他同function<T>一样都是包装可调用对象,但packaged_task<>还能通过future<>获取异步调用的结果。
using T = packaged_task<int(int)>; //可以考虑下用function<int(int)>情况是怎样的
std::deque<T> task_q;
std::mutex mu;
std::condition_variable cond;
int factorial(int i)
{
return i*i*i;
}
void thread_1()
{
T t;
{
std::unique_lock<std::mutex> locker(mu);
cond.wait(locker, []() { return !task_q.empty(); });
t = std::move(task_q.front()); //只能移动,拷贝构造是被删除的
task_q.pop_front();
}
t(3); //用function<int(int)>只能这里获取结果
}
int main()
{
std::thread t1(thread_1);
T t(factorial);
future<int> fui = t.get_future();
{
std::lock_guard<std::mutex> locker(mu);
task_q.push_back(std::move(t));
}
cond.notify_one();
t1.join();
cout << "异步调用结果:" << fui.get() << endl; //用packaged_task<>包装后,主线程能获取到执行结果
return 0;
}
四、future<>、shared_future<>
future<T>获取结果用get(),不获取结果用wait();但只能get()一次,要多次get()需要用shared_future<>.
//第一个线程进行耗时运算
int threadadd(int a, int b)
{
this_thread::sleep_for(chrono::seconds(2));
return a + b;
}
//一下两个线程需要线程1的计算结果
void thread1( shared_future<int>& sf )
{
cout << "thread1 id=" << this_thread::get_id() << " sf.get()=" << sf.get() << endl;
}
void thread2(shared_future<int>& sf)
{
cout << "thread1 id=" << this_thread::get_id() << " sf.get()=" << sf.get() << endl;
}
int main()
{
packaged_task<int(int,int)> pt(threadadd);
future<int> fui = pt.get_future();
shared_future<int> sfu = fui.share(); //通过share()转成shared_future
thread at(std::move(pt), 6, 7); //packaged_task包装一个函数指针,目的是能获取函数运行结果
thread t1(thread1,std::ref(sfu)); //以下两个线程都需要at线程的运行结果
thread t2(thread2, std::ref(sfu)); //这里的参数一定要是shared_future,因为future<>::get()只能一次(所以是移动)
at.join();
t1.join();
t2.join();
return 0;
}
|