夯实基础(4):std::async函数

多线程编程环境下,一般使用std::thread类来创建子线程,但有时系统资源紧张,或者可能需要子线程返回一些结果。这种情况下,可以使用std::async(),该函数可以根据具体情况确定是否创建新的线程(即启动一个异步任务);可以将返回值保存在类型为std::future(一个类模板)的对象中。

#include<iostream>
#include<future>
int tmain(){
    std::cout<<"tmain, thread id = "<<std::this_thread::get_id()<<std::endl;
    return 5;
}

int main(){
    std::cout<<"main, thread id = "<<std::this_thread::get_id()<<std::endl;
    std::future<int>res = std::async(tmain);
    cout<<res.get()<<endl;//阻塞,等待返回值
    std::cout<<"main end"<<std::endl;
    return 0;
}
复制代码

std::async()函数可能会创建一个新线程来执行入口函数tmain(),然后将结果返回给变量res。该函数可以有额外的参数:std::launch::deferredstd::launch::async,前者表示将线程入口函数的执行延迟到get()方法调用时,但如果没有调用get()方法,则相当于没有这条语句,此时可以发现没有创建新线程;后者则会创建处新线程。不加参数的情况相当于std::launch::deferred|std::launch::async,此时系统根据具体情况决定是否创建新线程。

//main()
#include<iostream>
#include<future>
int tmain1(int val){
    std::cout<<"tmain1, thread id = "<<std::this_thread::get_id()<<std::endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    std::cout <<"tmain1: "<< getTime() << std::endl;
    return val;
}
int main(){
    std::cout<<"main, thread id = "<<std::this_thread::get_id()<<endl;
    std::future<int>res1 = std::async(std::launch::deferred,tmain1,6);
    std::future<int>res2 = std::async(std::launch::async,tmain1,8);
    std::cout<<res2.get()<<std::endl;
    std::cout<<res1.get()<<std::endl;
    std::cout<<"main end"<<std::endl;
    return 0;
}
复制代码

上述代码中,res1变量所在行没有创建新线程,而res2变量所在行创建了新线程,如下图。同时,先阻塞在res2get()方法处,故先输出8,后阻塞在res1get()方法处,然后输出6。
image.png

std::future还可以进一步判断线程是否执行完毕或者是否被延迟执行:

...
int main(){
    std::cout<<"main, thread id = "<<std::this_thread::get_id()<<endl;
    std::future<int>res = std::async(tmain1,6);
    std::future_status status = res.wait_for(std::chrono::seconds(1));//程序在此处等待1秒
    if(status == std::future_status::timeout){//创建新线程
        std::cout<<"线程执行超时"<<std::endl;
        std::cout<<res.get()<<std::endl;
    }
    else if(status == std::future_status::timeout){//创建新线程
        std::cout<<"线程执行完成"<<std::endl;
        std::cout<<res.get()<<std::endl;
    }
    else//if(status == std::future_status::deferred)//不创建新线程
    {
        std::cout<<"线程延迟执行"<<std::endl;
        std::cout<<res.get()<<std::endl;//入口函数在此处执行并返回结果
    }
    std::cout<<"main end"<<std::endl;
}
复制代码

上述代码中,wait_for()函数的等待时间可以理解成分配给线程的执行时间(如果创建了新线程),由于线程入口函数中沉睡了5s,大于等待时长,所以上述代码执行第一个if语句,否则,执行第二个if语句。但即使线程没有执行完,也会阻塞在get()方法处等待结果返回。如果async()函数使用了std::launch::deferred参数,则wait_for()无效,会执行第三个if语句。上述代码事实上可以确定async()函数不带参数或者带两个参数时带来的不确定性问题,即异步任务是否被延迟执行。
wait_for()函数原型如下:

#define std::future_status std::_Future_status
template<class _Rep, class _Per>
_Future_status wait_for(const chrono::duration<_Rep,_Per>&_Rel_time)const{
    if(!valid())
        _Throw_future_error(make_error_code(future_errc::no_state));
    return (_Assoc_state->_Wait_for(_Rel_time));
}
_Future_status _Wait_for(const chrono::duration<_Rep,_Per>&_Rel_time){
    unique_lock<mutex>_Lock(_Mtx);
    if(_Has_deferred_function())
        return _Future_status::deferred;
    if(_Cond.wait_for(_Lock,_Rel_time,_Test_ready(this)))
        return _Future_status::ready;
    return _Future_status::timeout;
}
_Future
复制代码

从上上面代码中第二个函数可以很容易看出来,如果检测到std::launch::deferred参数,则直接返回deferred状态;否则,等待_Rel_time并使用_Test_ready检测是否有超时信号,如果没有,在返回ready状态,否则,返回timeout状态。

猜你喜欢

转载自juejin.im/post/7059604972198952973