[C++11 并发编程] 14 关联任务与期望

来源:互联网 发布:类似于知乎的网站 编辑:程序博客网 时间:2024/06/10 09:33

std::packaged_task<>将期望绑定到一个函数或者可调用对象。当std::packaged_task<>对象被触发时,它将调用关联的函数和可调用对象使得期望被满足,并将返回值填入期望关联的数据之中。这个可以用于构建线程池,也可以用于任务管理(每个任务在各自的线程中执行或所有任务顺序的在一个后台线程中执行)。如果一个大的操作可以被拆分为多个子任务,每个子任务就可以被放入一个std::packaged_task<>实例之中,再将这个实例交给任务调度器或者线程池。这样调度器就可以只与std::packaged_task<>实例打交道而不是与具体的函数打交道。

std::packaged_task<>模版类的模版参数是一个函数签名,比如void()就是一个没有输入参数,返回值为空的函数。传递给std::packaged_task<>的函数返回值也带表了get_future()成员函数的额返回值。函数签名的参数列表则用于指明任务函数的调用操作符。std::packaged_task<std::string(std::vector<char>*,int)>的部分声明如下:

template<>class packaged_task<std::string(std::vector<char>*,int)>{public:    template<typename Callable>    explicit packaged_task(Callable&& f);    std::future<std::string> get_future();    void operator()(std::vector<char>*,int);};
std::packaged_task对象也是一个可调用对象,可被传入给std::function对象,传给std::thread对象作为线程函数,传给其它需要一个可调用对象的函数,也可以直接被调用。当std::packaged_task被作为函数对象调用时,传递给operator()的参数将被传递给packaged_task嗦包含的函数,返回值作为异步操作的结果被存入std::future中,通过get_future()可以得到这个结果。执行std::packaged_task后,在需要执行结果时,可以等待期望被满足。

下面是一个具体的例子。很多GUI框架需要由特定的线程来完成GUI的更新。如果其它线程需要更新GUI,它必须发送一个消息给正确的线程。std::packaged_task提供了一个不需要为每种GUI相关行为自定义消息的实现方案:

#include <iostream>#include <deque>#include <mutex>#include <future>#include <thread>#include <utility>#include <chrono>std::mutex m;std::deque<std::packaged_task<void()> > tasks;static int shutdown = 3;bool gui_shutdown_message_received(){// 执行3次则结束GUI线程 return ((shutdown--) < 0);}void get_and_process_gui_message(){}// 倒计时3秒 void countdown(){int from = 3, to = 0;  for (int i=from; i!=to; --i){    std::cout << i << '\n';    std::this_thread::sleep_for(std::chrono::seconds(1));  }  std::cout << "Lift off!\n";}// GUI线程void gui_thread(){// 循环直到收到结束消息     while(!gui_shutdown_message_received())    {    std::cout << "gui_thread" << std::endl;    // 获取GUI消息并处理         get_and_process_gui_message();        std::packaged_task<void()> task;        {            std::lock_guard<std::mutex> lk(m);            // 如果队列中没有任务,执行下一次循环             if(tasks.empty())                continue;            // 从队列中获取任务             task=std::move(tasks.front());            tasks.pop_front();        }        // 释放mutex,执行任务         task();    }}std::thread gui_bg_thread(gui_thread);template<typename Func>std::future<void> post_task_for_gui_thread(Func f){std::cout << "post_task_for_gui_thread" << std::endl;// 创建packaged_task,任务函数为countdown     std::packaged_task<void()> task(f);    // 从任务获取期望     std::future<void> res=task.get_future();    std::lock_guard<std::mutex> lk(m);    // 将任务加入到队列中     tasks.push_back(std::move(task));    return res;}int main(){std::cout << "main" << std::endl;std::future<void> f = post_task_for_gui_thread(countdown);std::cout << "posted 1" << std::endl;post_task_for_gui_thread(countdown);std::cout << "posted 2" << std::endl;// 等待第一个GUI线程的任务执行完成 f.get();std::cout << "got" << std::endl;    gui_bg_thread.join();}
程序执行效果如下,main函数中,创建两个post_task_for_gui_thread对象,当GUI线程执行时,先执行一次倒计时任务,第一次倒计时任务执行完毕,main线程继续执行(打印got),然后执行第二次倒计时任务。

maingui_threadpost_task_for_gui_threadposted 1post_task_for_gui_threadposted 2321Lift off!gui_threadgot321Lift off!gui_threadgui_thread--------------------------------Process exited after 6.243 seconds with return value 0请按任意键继续. . .
这个例子使用std::packaged_task<void()>创建任务,其包含了一个无参数无返回值的函数或可调用对象(如果当这个调用有返回值时,返回值会被丢弃)。这可能是最简单的任务,如你之前所见,std::packaged_task也可以用于一些复杂的情况——通过指定一个不同的函数签名作为模板参数,你不仅可以改变其返回类型(因此该类型的数据会存在期望相关的状态中),而且也可以改变函数操作符的参数类型。这个例子可以简单的扩展成允许任务运行在图形界面线程上,且接受传参,通过std::future返回值,而不仅仅作为任务完成一个标志。
如果有些任务不能用简单的函数调用来表示,则可以使用第三种“期望”来解决:使用std::promise对值进行显示设置。



0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 饥荒渡渡鸟死了一只怎么办 饥荒电脑联机植物生病怎么办 开车蹭到别人车怎么办 立定跳不会收腹怎么办 1岁宝宝有蛔虫怎么办 手机屏幕总是有网页跳出来怎么办 cs游戏屏幕变成正方形怎么办 大王卡被收回了怎么办 模拟人生4小人生病了怎么办 创造与魔法死后怎么办? 脚不小心扭伤了该怎么办 小鸡脚扭伤了该怎么办 跳高比赛最终成绩相等怎么办 热车1200怠速降不下来怎么办 大腿根骨髓水肿越来越疼怎么办 倒库方向打晚了怎么办 签吻芳颜祛斑液脸脱皮怎么办 3d右边工具栏消失了怎么办 3d菜单栏消失了怎么办 觉得自己性无能不敢谈对象怎么办 护士面试时被问到病人坠床怎么办 三次元仪器坏了怎么办 运动同手同脚怎么办 狗狗突然害怕不敢走路怎么办 一岁半宝宝因为害怕不敢走路怎么办 猫把背拱起来怎么办 穿猫跟鞋走路不稳怎么办 狗狗后腿内八字怎么办 快走后小腿粗了怎么办? 猫的嘴巴烂了怎么办 苹果8丢了已关机怎么办 肚子吃多了难受怎么办 喝水喝的肚子胀怎么办 肚子吃撑了难受怎么办 肚子撑得想吐怎么办 吃饭吃的太饱怎么办 吃饭吃的太撑怎么办 跑步迈不开步子怎么办 踏步走步子反了怎么办 微信不支持计步怎么办 肝癌二次介入后头晕心慌怎么办