一、std::async和std::future
1.1 函数示例
async
是一个函数模板,可以启动一个异步任务
,启动这个异步任务后可以返回一个std::future
对象。
- 启动一个异步任务是指——自动创建一个线程并开始执行对应的线程入口函数,返回一个future对象。
这个
future
对象里面含有线程入口函数所返回的结果,这个结果会在线程执行完毕的时候拿到。
本质上,是把std::future
对象和线程mythread
绑定在一起了。所以执行到result.get()
的时候,一定要等到mythread
执行完,才可以继续执行下去,如果mythread
没有执行完,会停在那里等待。
可以看到,先执行了主线程,但是先继续执行continue
(即使先调用了async(mythread)
),然后是子线程打印,需要get()
获取子线程的结果,所以停下来等待,然后线程将结果返回给future对象result
。
- 本质上就是通过
future
对象的get()成员函数
来等待线程结束并返回结果,如果get()拿不到结果,就会卡在那里等着。还有个wait()
函数,也是等待线程返回,但是不能返回结果
。get()
只能调用一次。
1.2 std::lunch
有一个函数std::launch::deferred
可以延迟线程入口函数
调用到get()
调用的时候才执行。
因为上面我们发现,就算不调用get(),主线程执行完了,也会在return 0
那里等待子线程执行完。如果我们加了launch
延迟,会导致子线程
根本不会执行,因为没有调用成员函数get()
。
- 本质上,有
launch::deferred
后,线程都没有创建出来。
但是!如果调用了get()
(在基于std::launch::deferred
的情况下),会发现主线程ID和子线程ID是一样的!
- 本质上说明
deferred
这个延迟
调用并没有创建子线程
,是在主线程
中调用的线程入口函数
。
1.3 std::launch::async
std::launch::async
这个标记,可以让在调用async
函数的时候就立即开始
创建线程,而不需要等待执行到get()
的时候再调用,而且会创建一个新的线程。
二、std::packaged_task
这是一个类模板,模板参数是各种可调用对象
,通过packaged task
可以把各种可调用对象包装起来,然后作为线程入口函数
使用。
三、std::promise
std::promise
是一个类模板
可以在某个线程中给它赋值,然后可以在其他线程中,把这个值取出来用。
通过promise和future实现2个线程的消息传递
小结
博客示例源代码
- 单独的函数作为线程入口
#include
#include
#include
#include
#include
#include
using namespace std;
int mythread()
{
cout << "mythread() start" << "thread_id = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end" << "thread_id = " << std::this_thread::get_id() << endl;
return 5;
}
int main()
{
cout << "main" << "thread_id = " << std::this_thread::get_id() << endl;
std::future result = std::async(mythread);
cout << "continue...!" << endl;
int def;
def = 0;
cout << result.get() << endl;
cout << "I love China" << endl;
return 0;
}
- 类对象作为线程入口函数(async和deferred)
#include
#include
#include
#include
#include
#include
using namespace std;
class A
{
public:
int mythread(int mypar)
{
cout << mypar << endl;
cout << "mythread() start" << "thread_id = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end" << "thread_id = " << std::this_thread::get_id() << endl;
return 5;
}
};
int main()
{
A a;
int tmp_par = 12;
cout << "main" << "thread_id = " << std::this_thread::get_id() << endl;
std::future result = std::async(std::launch::async, &A::mythread, &a, tmp_par);
cout << "continue...!" << endl;
int def;
def = 0;
cout << result.get() << endl;
cout << "I love China" << endl;
return 0;
}
- package_task
#include
#include
#include
#include
#include
#include
using namespace std;
int mythread(int mypar)
{
cout << mypar << endl;
cout << "mythread() start" << "thread_id = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end" << "thread_id = " << std::this_thread::get_id() << endl;
return 5;
}
int main()
{
cout << "main" << "thread_id = " << std::this_thread::get_id() << endl;
std::packaged_task mypt(mythread);
std::thread t1(std::ref(mypt), 1);
t1.join();
std::future result = mypt.get_future();
cout << result.get() << endl;
cout << "I love China!" << endl;
return 0;
}
- packaged_task直接调用
int main()
{
cout << "main" << "thread_id = " << std::this_thread::get_id() << endl;
std::packaged_task mypt([](int mypar)
{
cout << mypar << endl;
cout << "mythread() start" << "thread_id = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end" << "thread_id = " << std::this_thread::get_id() << endl;
return 5;
}
);
// 直接调用,相当于函数调用,是同一个线程。
mypt(105);
std::future result = mypt.get_future();
cout << result.get() << endl;
cout << "I love China!" << endl;
return 0;
}
- 使用了移动语义(move)
vector > mytasks;
int main()
{
cout << "main" << "thread_id = " << std::this_thread::get_id() << endl;
std::packaged_task mypt([](int mypar)
{
cout << mypar << endl;
cout << "mythread() start" << "thread_id = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end" << "thread_id = " << std::this_thread::get_id() << endl;
return 5;
}
);
mytasks.push_back(std::move(mypt)); // 这里使用了move移动语义,而不是拷贝
// 移入之后,mypt就为空了
return 0;
}
- future和promise
#include
#include
#include
#include
#include
#include
using namespace std;
void mythread(std::promise& tmpp, int calc)
{
calc ++;
calc *= 10;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
int result = calc;
tmpp.set_value(result); // 将结果保存到tmpp这个对象中
}
int main()
{
cout << "main" << "thread_id = " << std::this_thread::get_id() << endl;
// 声明一个std::promise对象myprom,保存值的类型为int
std::promise myprom;
// 使用thread的话,一定要用join(),不然会报异常
std::thread t1(mythread, std::ref(myprom), 180);
t1.join();
// 将promise和future绑定,用于获取线程返回值
std::future fu1 = myprom.get_future();
auto result = fu1.get(); // get就是可以得到线程结果
// 通过promist保存一个值(set_value()),在将来的某个时刻通过
// 把一个future绑定到promise上得到之前存的值
cout << "result = " << result << endl;
cout << "I Love China!" << endl;
return 0;
}
- 通过future和promise实现thread1和thread2的消息传递
#include
#include
#include
#include
#include
#include
using namespace std;
void mythread(std::promise& tmpp, int calc)
{
calc ++;
calc *= 10;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
int result = calc;
tmpp.set_value(result); // 将结果保存到tmpp这个对象中
}
void mythread2(std::future &tmpf)
{
auto result = tmpf.get();
cout << "mythread2 result" << result << endl;
return;
}
int main()
{
cout << "main" << "thread_id = " << std::this_thread::get_id() << endl;
// 声明一个std::promise对象myprom,保存值的类型为int
std::promise myprom;
// 使用thread的话,一定要用join(),不然会报异常
std::thread t1(mythread, std::ref(myprom), 180);
t1.join();
// 将promise和future绑定,用于获取线程返回值
std::future fu1 = myprom.get_future();
std::thread t2(mythread2, std::ref(fu1));
t2.join(); // 等待mythread2线程执行完毕
cout << "I Love China!" << endl;
return 0;
}