首页 > C++多线程:package_task异步调用任何目标执行操作

C++多线程:package_task异步调用任何目标执行操作

文章目录

      • 描述
      • 函数成员及使用
      • 总结


我们上一篇描述关于C++多线程中的异步操作相关库( asyncpromise),本节将分享c++标准库中最后一个多线程异步操作库 package_task的学习笔记。

描述

  • 头文件

  • 声明方式: template< class R, class ...Args > class packaged_task;

  • 简介

    package_task标准类模版包装了任何可调用的目标,其中包括函数,std::bind表达式,lamda表达式或者其他函数对象。并支持package_task对象调用者的异步调用。调用之后的返回值或者产生的异常能够被存储在能够被std::future对象访问的共享状态中。

    综上描述,我们很明显能够体会到该模版类提供的功能和promise类非常接近,但是promise类没有办法初始化所有可调用的对象,promise类仅提供共享状态的多种访问机制并提供线程之间变量的共享机制。

函数成员及使用

  • 构造函数

    packaged_task() noexcept;构造无任务且无共享状态的package_task对象

    template explicit packaged_task( F&& f ) 构造拥有共享状态和任务副本的 std::packaged_task 对象,

    packaged_task( const packaged_task& ) = delete;复制构造函数被删除, std::packaged_task 仅可移动

    packaged_task( packaged_task&& rhs ) noexcept; rhs 之前所占有的共享状态和任务构造 std::packaged_task ,令 rhs 留在无共享状态且拥有被移动后的任务的状态

    查看如下代码:

    #include 
    #include 
    #include int fib(int n)
    { if (n < 3) return 1;else { std::cout << "fib  result " << (n-1)+(n-2) << std::endl;return (n-1) + (n-2);}
    }int main()
    { std::packaged_task<int(int)> fib_task(&fib); std::cout << "starting task
    ";//此时已经将package_task调用对象的执行返回值转交给future对象,所以后续//的线程执行由惰性赋值来触发。即当future的对象的共享状态尝试获取线程执行//结果的时候才进行线程执行,并返回结果。类似std::async的policy:std::launch::deferredauto result = fib_task.get_future(); std::thread t(std::move(fib_task), 40);std::cout << "waiting for task to finish...
    ";std::cout << result.get() << '
    ';std::cout << "task complete
    ";t.join();
    }
    

    输出如下:

    starting task
    waiting for task to finish...
    fib  result 77
    77
    task complete
    
  • 析构函数~packaged_task()

    抛弃共享状态并销毁存储的任务对象,同 std::promise::~promise ,若在令共享状态就绪前抛弃它,则存储以 std::future_errc::broken_promise 为 error_code 的 std::future_error 异常

  • 赋值运算符

    packaged_task& operator=( const packaged_task& ) = delete

    复制赋值运算符被删除, std::packaged_task 仅可移动

  • std::packaged_task::get_future返回与 *this 共享同一共享状态的 future

    promise类一样,get_future 只能对每个 packaged_task 调用一次

    get_future成员出现异常的情况如下:

    • 已通过调用 get_future 取得共享状态。设置 error_category 为 future_already_retrieved
    • *this 无共享状态。设置 error_category 为 no_state
  • std::packaged_task::make_ready_at_thread_exit成员函数

    以转发的 args 为参数调用存储的任务。任务返回值或任何抛出的异常被存储于 *this 的共享状态。

    仅在当前线程退出,并销毁所有线程局域存储期对象后,才令共享状态就绪

    代码如下

    #include 
    #include 
    #include 
    #include 
    #include 
    #include void worker(std::future<void>& output)
    { std::packaged_task<void(bool&)> my_task{  [](bool& done) {  done=true; } };auto result = my_task.get_future();bool done = false;//根据打印已经可以看到,此时已经执行了package_task的线程内容//但是共享状态到函数作用域结束之前并未就绪my_task.make_ready_at_thread_exit(done); // 立即执行任务std::cout << "worker: done = " << std::boolalpha << done << std::endl;auto status = result.wait_for(std::chrono::seconds(0));if (status == std::future_status::timeout)std::cout << "worker: result is not ready yet" << std::endl;output = std::move(result);
    }int main()
    { std::future<void> result;std::thread{ worker, std::ref(result)}.join();//等到线程函数返回结果,且future对象就绪之后,可以看到打印状态变为就绪auto status = result.wait_for(std::chrono::seconds(0));if (status == std::future_status::ready)std::cout << "main: result is ready" << std::endl;
    }
    

总结

综上对package_task的描述,我们可以看到package_task模版类就像promise一样可以被异步调用,并且将调用对象执行的结果封装在future的共享状态中,来让调用者获取。同时package_task的调用者获取调用对象的执行结果过程就像async的惰性求值策略,当调用者想要获取调用对象的执行结果时才开始执行调用对象。

package_task的优势更多是它能够初始化所有的可调用对象,并且支持对该调用对象的异步访问机制。

promise更多的优势是线程之间的变量的传递,同时返回future类的共享状态。同时它也能够支持多种共享状态的访问机制,惰性求值/立即执行。

所以C++多线程的几个异步操作promiseasyncpackage_task都可以进行线程之间的异步操作,希望以此笔记在今后多线程编程中各持所需进行学习。

更多相关:

  • 经过长期探索,发现一个不需要手动设置线程休眠时间(e.g. std::this_thread::sleep_for(std::chrono::microseconds(1)))的代码: Github: https://github.com/log4cplus/ThreadPool #ifndef THREAD_POOL_H_7e...

  • nth_element(first,nth,last) first,last 第一个和最后一个迭代器,也可以直接用数组的位置。  nth,要定位的第nn 个元素,能对它进行随机访问. 将第n_thn_th 元素放到它该放的位置上,左边元素都小于它,右边元素都大于它. 测试代码: http://www.cplusplus.com...

  • c/c++老版本的rand()存在一定的问题,在转换rand随机数的范围,类型或者分布时,常常会引入非随机性。 定义在 中的随机数库通过一组协作类来解决这类问题:随机数引擎 和 随机数分布类 一个给定的随机数发生器一直会生成相同的随机数序列。一个函数如果定义了局部的随机数发生器,应该将(引擎和分布对象)定义为 st...

  • jsoncpp 是一个C++ 语言实现的json库,非常方便得支持C++得各种数据类型到json 以及 json到各种数据类型的转化。 一个json 类型的数据如下: {"code" : 10.01,"files" : "","msg" : "","uploadid" : "UP000000" } 这种数据类型方便我们人阅读以...

  • 问题如下: 已知一组数(其中有重复元素),求这组数可以组成的所有子集中,子 集中的各个元素和为整数target的子集,结果中无重复的子集。 例如: nums[] = [10, 1, 2, 7, 6, 1, 5], target = 8 结果为: [[1, 7], [1, 2, 5], [2, 6], [1, 1, 6]] 同样之前有...