zoukankan      html  css  js  c++  java
  • C11线程管理:异步操作

     1、异步操作

      C++11提供了异步操作相关的类,std::future、std::promise和std::package_task。std::future作为异步结果的传输通道,方便的获取线程函数的返回值;std::promise用来包装一个值,将数据和future绑定起来,方便线程赋值;std::pack_age用来包装一个可调用对象,将函数和future绑定起来,以便异步调用。

    1.1 std::future

      c11中的线程使我们创建和使用线程非常方便,但是当我们想要得到线程函数的执行结果是比较麻烦的,不能直接通过thread.join()来得到结果,需要定义一个变量,在线程里面进行赋值,然后执行join来得到结果。

      所以,thread库提供了future来访问异步操作的结果,因为是等待线程执行完,在未来获取,所以被成为future,future提供了获取异步操作结果的通道,可以通过同步等待来获取操作结果,并可以查询future的状态来获取异步操作的结果。

      future的状态:

      Deferred,异步操作还未开始;
      Ready,异步操作已经完成;
      Timeout,异步操作超时。

     //查询状态
     std::future_status status;
     do
     {
        status = future.wait_for(std::chrono::seconds(1));
        if(status == std::future_status::deferred)
        {
            //do sommething when deferred
        }
        else if(status == std::future_status::ready)
        {
            //do sommething when ready
        }
        else
        {
            //do sommething when timeout
        }
     }while(status != std::future_status::ready)

       获取future有三种方式:get,wait,wait_for,其中get等待异步操作结束并返回结果,wait只是等待异步操作完成,没有返回值;wait_for是超时等待返回结果。

     1.2 std::promise

      std::promise可以协助线程赋值,将数据与future绑定起来,为获取线程中某个值提供便利,在线程函数外面传进来的promise赋值,线程函数执行完成之后,通过promise绑定的future来获取该值。取值是间接通过promise内部提供的future来获取的。

         std::promise<int> pr;
        std::thread t([](std::promise<int> & p){ p.set_value_at_thread_exit(9); }, std::ref(pr));
    
        std::future<int> f = pr.get_future();
        int i = f.get();
        //执行后 i = 9

     1.3 std::package_task

       std::package_task包装一个可调用对象的包装类(如function,lambda expression,bind expression,和其它的函数对象),将函数和future绑定起来,以便异步调用,它和promise的区别是,promise绑定的是一个变量,而package_task则是绑定的一个函数。

        std::packaged_task<int()> task([](){ return 5; });
        std::thread t1(std::ref(task));
    
        std::future<int> f1 = task.get_future();
        int i = f1.get();
        //执行后 i = 5

     1.4 三者关系

      future提供一个访问线程异步操作结果的机制,它和线程是一个级别的,属于最底层。package_task和promise则是内部都有future,promise是包装一个值,而package_task是包装一个操作对象,具体使用哪个要根据实际情况来做区分。

      需要注意的是,future是不可拷贝的,只能移动,shared_future可以拷贝,如果需要放到容器或者其他地方,则需要用到shared_future。

     2、线程异步操作std::async

      std::async比std::promise、std::package_task和std::thread更上层,它可以用来直接创建异步的task,异步任务返回的结果保存在future中,当需要获取线程执行的结果,可以通过future.get()来获取,如果不关注异步任务的结果,只是简单的等待任务执行完成,则调用future.wait()即可。

      std::async是更高层次的异步操作,使我们不关心线程创建的内部细节,就能方便的获取线程异步执行的结果,还可以指定线程创建策略,更多的时候应该使用 std::async来创建线程,成为异步操作的首选。

      std::async原型为std::async(std::launch::async | std::launch::deferred,f,args...),第一个参数为线程的创建策略,第二个为线程函数,其他的为线程函数的参数。

      关于创建策略有两种:

      std::launch::async:在调用async就开始创建线程;
      std::launch::deferred:延迟加载的方式创建线程,调用async的时候不创建线程,直到调用了future的get或者wait方法来创建线程。

    #include <thread>    
    #include <iostream>   
    #include <mutex>  
    #include <future>
    
    int main()
    {
        std::future<int> f1 = std::async(std::launch::async, [](){ return 8; });
        std::cout << f1.get() << std::endl; //output 8
    
        std::future<void> f2 = std::async(std::launch::async, [](){ std::cout << 8 << std::endl; });
        f2.wait(); //output 8
    
        std::future<int> f3 = std::async(std::launch::async, []()
        { 
            std::this_thread::sleep_for(std::chrono::seconds(3));
            return 8;
        });
    
        std::cout << "Wating..." << std::endl;
        std::future_status status;
        do
        {
            status = f3.wait_for(std::chrono::seconds(1));
            if (status == std::future_status::deferred)
            {
                std::cout << "deferred." << std::endl;
            }
            else if (status == std::future_status::timeout)
            {
                std::cout << "timeout." << std::endl;
            }
            else
            {
                std::cout << "ready." << std::endl;
            }
        } while (status != std::future_status::ready);
        std::cout << "result:" << f3.get() << std::endl;
    
        return 0;
    }
    //执行结果:
    8
    8
    Wating...
    timeout.
    timeout.
    ready.
    result:8
  • 相关阅读:
    s3fs 挂载minio为本地文件系统
    P5787 线段树分治
    P5494 线段树分裂
    P1552 [APIO2012]派遣
    CF600E Lomsat gelral(线段树合并)
    P5283 异或粽子
    P4735 最大异或和(可持久化 trie)
    P3960 列队
    bzoj4316 小C的独立集
    P5021 赛道修建
  • 原文地址:https://www.cnblogs.com/ChinaHook/p/7966961.html
Copyright © 2011-2022 走看看