c++多线程学习11 packaged_task与async

一、packaged_task异步调用函数打包

普通的函数一调用立马获取返回值
packaged_task类的主要作用是把一个函数包装成一个对象,通过这个对象的访问的时候在函数的调用和返回值的获取可以拆分成两步
简言之,将一个普通的可调用函数对象转换为异步执行的任务。

string TestPack(int index)
{
    
    
    cout << "begin Test Pack " << index << endl;
    this_thread::sleep_for(2s);
    return "Test Pack return";
}
int main(int argc, char* argv[])
{
    
    
    packaged_task< string(int) > task(TestPack);
    auto result = task.get_future();
    //task(100);
    thread th(move(task),101);
    
    cout << "begin result get" << endl;

    //测试是否超时
    for (int i = 0; i < 30; i++)
    {
    
    
        if (result.wait_for(100ms) != future_status::ready)
        {
    
    
            continue;
        }
    }
    if (result.wait_for(100ms) == future_status::timeout)
    {
    
    
        cout << "wait result timeout" << endl;
    }
    else
        cout << "result get " << result.get() << endl;
    th.join();
    getchar();
    return 0;
}
packaged_task< string(int) > task(TestPack);

< string(int) >为函数指针类型,string是返回类型,int为参数类型,TestPack为函数指针,通过task将TestPack这个函数包装起来,

auto result = task.get_future();

通过调用std::packaged_task的get_future成员将共享状态与std::future对象result关联。调用之后,两个对象共享相同的共享状态:(1).std::packaged_task对象是异步提供程序(asynchronous provider),应通过调用存储的任务(stored task)在某个时刻将共享状态设置为就绪。(2).std::future对象是一个异步返回对象,可以检索共享状态的值,并在必要时等待其准备就绪。

共享状态的生存期至少要持续到与之关联的最后一个对象释放或销毁为止。

 thread th(move(task),101);

启动线程:如果用左值的这样传给线程的是task的拷贝,所以先用move将task转为右值,此时不能再用以往的方法来调用,101为task绑定的函数的参数

result.wait_for(100ms) != future_status::ready

等待结果返回,wait_for可设置超时时间,如果在超时时间之内任务完成,则返回std::future_status::ready状态;如果在超时时间之内任务尚未完成,则返回std::future_status::timeout状态。
在这里插入图片描述
先在子线程中延时4秒才拿到结果:

this_thread::sleep_for(4s);

由于3.1秒后会get_future()所以get_future()返回的是future_status::timeout
在这里插入图片描述
改成2秒,在2秒后子线程就拿到了结果,此时再3.1秒处get_future()将会返回task所包装的函数指针指向函数的返回值:1111111111111111111:
在这里插入图片描述

二、 async 创建异步线程

在C++11中引入了async异步调用函数,其封装了异步(多线程)实现的复杂过程,封装了std::promise、std::packaged_task加上std::thread,基本上可以代替std::thread的所有事情。async将计算结果保存在future<>中,通过get()获取每个对象的最终结果

语法 auto future = async(launch::deferred, TestAsync,100);
返回一个future对象,第一个参数是启动方法,第二个参数是个函数指针,第三个参数是这个函数指针指向函数的参数

string TestAsync(int index)
{
    
    
    cout << index<<" begin in TestAsync " << this_thread::get_id() << endl;
    this_thread::sleep_for(2s);
    return "TestAsync string return";
}
int main(int argc, char* argv[])
{
    
    
   //创建异步线程
    
    cout << "main thread id " << this_thread::get_id() << endl;
    cout << endl;
   
    cout << "=====不创建线程直接启动异步任务====" << endl;
    auto future = async(launch::deferred, TestAsync,100);
    this_thread::sleep_for(3s);
    cout << "begin future get " << endl;
    cout << "future.get() = " << future.get() << endl;
    cout << "end future get" << endl;

    cout << endl;
    //创建异步线程
    cout << "=====创建异步线程====" << endl;
    auto future2 = async(TestAsync, 101);
    this_thread::sleep_for(2s);
    cout << "begin future2 get " << endl;
    cout << "future2.get() = " << future2.get() << endl;
    cout << "end future2 get" << endl;


    getchar();
    return 0;
}

在这里插入图片描述

三种异步启动方式:
(1)std::launch::async
它保证了异步行为,即传递的函数将在单独的线程中执行。
先显示begin in TestAsync再显示begin future get,且线程号不一致,将异步线程显示begin in TestAsync前延时三秒,由于异步任务通过异步线程调用,async的下一句与异步线程同时调用,将会先显示只延时2秒的begin future get
在这里插入图片描述
所以本方法是创建异步线程去完成异步任务,创建的一瞬间就去调用异步任务
(2)std::launch::deferred
非异步行为,即当其他线程将来调用get()以访问共享状态时,将调用Function。
在调用get()前不会去调用函数指针指向的函数,可以看到这里先延时3秒后先显示begin future get再去get(),函数指针指向的函数2秒延时后就会显示begin in TestAsync,但是在终端上可以看到是先显示begin future get再显示begin in TestAsync,且线程号一致,所以本方法是不创建线程直接启动异步任务,且只有在get()后才去调用异步任务
(3)std::launch::async | std::launch::deferred
它是默认行为。使用此启动策略,它可以异步运行或不异步运行,具体取决于系统上的负载。但是我们无法控制它。

对比:https://zhuanlan.zhihu.com/p/547500742

猜你喜欢

转载自blog.csdn.net/qq_42567607/article/details/126088976
今日推荐