C++11多线程
线程返回值
在先前的实践中,我们认识到,我们使用thread去获取线程返回值是比较费劲的。
C++11中提供了两个模板future和promise来协助我们获取线程返回值。
在使用它们的时候应该包含头文件thread。
异步任务(async)
异步是什么?
“异步与同步处理相对,异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程。” ------《百度百科》
例如一个线程A可能要执行很长时间,而此时另外一个与它无关联的线程B需要执行,它不会去阻塞B,若是A此时阻塞了B,而且B线程对实时性要求高,对用户来说就像是程序假死了一般,这是不合适的。
来看看我们如何创建一个异步线程。
#include <iostream>
#include <thread>
#include <future> //async所在的头文件
using namespace std;
//线程入口函数
int my_thread(){
cout << "the thread_Id is :" << this_thread::get_id() << endl;
std::chrono::milliseconds delay(5); //设置五秒的休眠时间
std::this_thread::sleep_for(delay); //休眠五秒
return 1;
}
int main(){
cout << "the main thread_Id is :" << this_thread::get_id() << endl;
//创建一个异步任务
std::future<int> res = async(std::launch::async | std::launch::deferred,my_thread);
//创建异步任务状态对象,并等待线程执行6秒
std::future_status status = res.wait_for(std::chrono::second(6)); //约定线程执行的时间
if(status == std::future::timeout){ //线程执行时间超过我们约定的时间
cout << "线程执行超时" << endl;
}else if(status == std::future_status::ready){ //在约定时间内完成
cout << "线程在指定时间内返回了" << endl;
cout << res.get() << endl; //返回值获取
}else if(status === std::future_status::deferred){ //async选择了延迟执行
cout << "该线程被延迟执行了" << endl;
cout << res.get() << endl; //返回值获取,不写这个,线程根本不会执行
}
return 0;
}
执行结果:
参数
std::async(),我们使用它来创建一个异步任务,它返回一个future对象(下面会说)。
注:它有时候并不创建一个线程,而是在某个线程调用get()时在该线程中执行(当参数为std::deferred的时候)
- std::launch::async。 它会强制创建一个线程(异步任务),并在新线程中执行。
- std::launch::deferred。它会将程序延迟执行,而且,如果没有wait和get在后面承接,它将不会执行,大家可以试试。(它不会创建一个新线程)。
- std::launch::async | std::launch::deferred。即我们在程序中使用的那个参数,它和不带任何参数效果一样(缺省参数),系统会自行决定,挑选合适的参数。(一般系统资源多的时候,使用 std::launch::async,少则使用std::launch::deferred)。
跟thread的比较
1,std::thread()创建线程,如果创建失败,程序将会抛出异常
2,std::thread()拿到返回值比较费劲
1,std::async()创建线程,如果系统资源紧张,它不会创建新线程,所以不会抛出异常,则异步任务会执行在调用get()的那个线程上
2, std::async()可调用内部函数来取得返回值(下面就会讲)
返回值获取实践
上面我们说了怎么创建一个异步任务。接下来,我们讲如何获取一个线程的返回值。
future
#include <iosteram>
#include <thread>
#include <future>
using namespace std;
int my_thread(int mypar){
cout << mypar << endl;
cout << "我的线程开始执行了,iD是:" << std::this_thread::get_id() << endl;
std::chrono::milliseconds cost(5000);
std::this_thread::sleep_for(cost);
cout << "我的线程结束执行了,iD是:" << std::this_thread::get_id() << endl;
return 5;
}
int main(){
//创建了一个future对象,并将一个异步任务与它绑定
std::future<int(int>> m_f = async(my_thread,88);
cout << m_f.get() << endl;
return 0;
}
future是一个记录异步事件值及执行状态的对象,状态上面已经演示了,它可以通过get方法获取其内部值(move语义,不能多次调用)。它由async,packaged_task和promise产生。
shared_future
如果想要多次调用该返回值,可以用shared_future,它的get()的语义是复制,所以可以多次调用。
#include <iostream>
#include <thread>
#include <future>
int my_thread(int num){
int ans = num;
return ans;
}
int main(){
std::shared_future<int(int)> m_f_s = async(my_thread,18);
for(int i = 0;i < 10;++i){
cout << m_f.get() << endl;
}
return 0;
}
成员函数
get() 会阻塞其他线程,等待线程入口函数执行完,并返回结果,才放弃阻塞,它不能多次调用
wait() 会等待异步操作完成,没有返回值
wait_for() 会超时等待返回结果
promise
#include <iostream>
#include <thread>
#include <future>
using namespace std;
int my_thread(std::promise<int> &pro,int calc){
//假设这五秒我们都在做复杂处理
std::chrono::milliseconds cost(5000);
std::this_thread::sleep_for(cost);
//计算出了结果
int ans = calc; //报存结果
num.set_value(ans); //保存结果到一个promise对象中
return ans;
}
int main(){
promise<int> my_prom; //声明一个promise对象,保存的类型为int
thread th(my_thread,std::ref(myprom),88); //声明一个带promise的线程
th.join();
//结果获取
std::future<int> fue = myprom.get_future(); //将myprom和future绑定,用于获取返回值
int res = fue.get(); //获取值
cout << "返回值是: " << res << endl;
return 0;
}
promise是特殊的future,它的对象是异步provider,它可以在某一时刻,设置共享状态的值(set_value())。
成员函数
get_future() 返回一个与promise共享状态的future对象,返回的future对象可以访问由promise对象设置在共享状态上的值或者某个异常对象。
set_value() 设置共享状态的值,此后promise的共享状态标志变为ready
set_exception() 为 promise 设置异常,此后 promise 的共享状态变标志变为 ready。
swap() 交换promise的共享状态
packaged_task
#include <iostream>
#include <thread>
#include <future>
using namespace std;
int my_thread(int num){
std::chrono::milliseconds delay(5000);
std::this_thread::sleep_for(delay);
int ans = num;
return ans;
}
int main(){
//通过packaged_task将my_thread包装起来,
packaged_task<int(int)> my_pt(my_thread);
thread th(std::ref(my_pt),10);
th.join();
//将future和packaged绑定
future<int> res = my_pt.get_future();
cout << res.get() << endl;
return 0;
}
packaged用来包装一个可调用对象,它的对象是一个异步provider,它在某一时刻通过调用被包装的对象来设置共享状态的值。它的结果需传递给一个future对象去获取返回值。
成员函数
get_future() 返回一个与packaged_task共享状态的future对象,返回的future对象可以获得由另外一个线程在该packaged_task对象的共享状态下设置的值或者异常。
vaild() 检查当前packaged_task是否与一个由小的状态互相关联,对于由默认构造函数生成的packaged_task对象,该函数返回false,除非中间进行了move复制操作或者swap操作。
reset() 重置packaged_task的共享状态,但是保留之前的被包装的任务
swap() 交换packaged_task的共享状态
总结
我们可以通过对future的使用可以比较容易地从线程中获取返回值,而且,在异步任务的状态下,我们可以避免一些由于某线程运行时间过长,导致程序假死(通过对异步任务状态的控制)。
在写这个的时候,参考了大佬的几篇博客:大佬的地址