等待一次性事件,并发实战

多线程 等待一次性事件 packaged_task用法

多线程 等待一次性事件 std::promise用法

std::future和std::promise

背景:不是很明白,不知道为了解决什么业务场景,感觉std::asynck可以优雅的搞定一切,一次等待性事件,为什么还有个packaged_task。

背景:不是很明白,不知道为了解决什么业务场景,感觉std::async可以优雅的搞定一切的一次等待性事件,为什么还有个std::promise。

std::future

std::future期待一个返回,从一个异步调用的角度来说,future更像是执行函数的返回值,C++标准库使用std::future为一次性事件建模,如果一个事件需要等待特定的一次性事件,那么这线程可以获取一个future对象来代表这个事件。
异步调用往往不知道何时返回,但是如果异步调用的过程需要同步,或者说后一个异步调用需要使用前一个异步调用的结果。这个时候就要用到future。
线程可以周期性的在这个future上等待一小段时间,检查future是否已经ready,如果没有,该线程可以先去做另一个任务,一旦future就绪,该future就无法复位(无法再次使用这个future等待这个事件),所以future代表的是一次性事件

用法:和std::async一样,也能够返回std::future,通过调用get_future方法。也可以通过future得到线程的返回值。

用法:和std::async一样,也能够返回std::future,通过调用get_future方法。也可以通过future得到线程的返回值。

future的类型

<future>库的头文件中声明了两种future,唯一future(std::future)和共享future(std::shared_future)这两个是参照std::unique_ptr和std::shared_ptr设立的,前者的实例是仅有的一个指向其关联事件的实例,而后者可以有多个实例指向同一个关联事件,当事件就绪时,所有指向同一事件的std::shared_future实例会变成就绪。

特点:

特点:

future的使用

std::future是一个模板,例如std::future<int>,模板参数就是期待返回的类型,虽然future被用于线程间通信,但其本身却并不提供同步访问,热门必须通过互斥元或其他同步机制来保护访问。
future使用的时机是当你不需要立刻得到一个结果的时候,你可以开启一个线程帮你去做一项任务,并期待这个任务的返回,但是std::thread并没有提供这样的机制,这就需要用到std::async和std::future(都在<future>头文件中声明)
std::async返回一个std::future对象,而不是给你一个确定的值(所以当你不需要立刻使用此值的时候才需要用到这个机制)。当你需要使用这个值的时候,对future使用get(),线程就会阻塞直到future就绪,然后返回该值。

#include <future>#include <iostream>int find_result_to_add(){    return 1 + 1;}void do_other_things() {    std::cout << "Hello World" << std::endl;}int main(){    std::future<int> result = std::async(find_result_to_add);    do_other_things();    std::cout << result.get() << std::endl;    return 0;}

跟thread类似,async允许你通过将额外的参数添加到调用中,来将附加参数传递给函数。如果传入的函数指针是某个类的成员函数,则还需要将类对象指针传入(直接传入,传入指针,或者是std::ref封装)。
默认情况下,std::async是否启动一个新线程,或者在等待future时,任务是否同步运行都取决于你给的参数。这个参数为std::launch类型

  • std::launch::defered表明该函数会被延迟调用,直到在future上调用get为止
  • std::launch::async,表明函数会在自己创建的线程上运行
  • std::launch::any = std::launch::defered | std::launch::async
  • std::launch::sync = std::launch::defered

enum class launch{    async,deferred,sync=deferred,any=async|deferred};

PS:默认选项参数被设置为std::launch::any。如果函数被延迟运行可能永远都不会运行。

1,是个模板类,模板类型是个方法类型,比如double,有一个参数,类型是int,返回值类型是double。

std::packaged_task<double> task;//func是个方法,有一个参数,类型是int,返回值类型是double

1,是个模板类,模板类型是个方法类型,比如double,有一个参数,类型是int,返回值类型是double。

std::promise<int> pro;//pro.get_future.get()的返回值为int类型

std::packaged_task

如果说std::async和std::feature还是分开看的关系的话,那么std::packaged_task就是将任务和feature绑定在一起的模板,是一种封装对任务的封装。

The class template std::packaged_task wraps any Callable target (function, lambda expression, bind expression, or another function object) so that it can be invoked asynchronously. Its return value or exception thrown is stored in a shared state which can be accessed through std::future objects.

可以通过std::packaged_task对象获取任务相关联的feature,调用get_future()方法可以获得std::packaged_task对象绑定的函数的返回值类型的future。std::packaged_task的模板参数是函数签名
PS:例如int add(int a, intb)的函数签名就是int

#include <future>#include <iostream>int add(int a, int b){    return a + b;}void do_other_things() {    std::cout << "Hello World" << std::endl;}int main(){    std::packaged_task<int> task;    do_other_things();    std::future<int> result = task.get_future();    task; //必须要让任务执行,否则在get()获取future的值时会一直阻塞    std::cout << result.get() << std::endl;    return 0;}

2,直接执行std::packaged_task的对象task时,不是异步执行,是在原来的线程上阻塞执行,也就是说,只有task执行结束后,后面的代码才能被执行,也就是说不是多线程执行。

std::packaged_task<std::string> task1(call_texi);std::future<std::string> ft1 = task1.get_future();task1;//task1执行完成后,才能执行下面的打印输出的代码,不是在新的线程里执行task1std::cout << "111111111111111111111111111111" << std::endl;

2,在std::promise<T>的对象上,调用set_value后,future变成就绪状态,调用future对象的get方法的地方就由阻塞变为不阻塞了。

std::promise<int> pro;pro.set_value;

void thread1(std::promise<int>& p, int val){  std::cout << "in thread1" << std::endl;  p.set_value;}std::promise<int> pro;std::future<int> ft1 = pro.get_future();std::thread t1(thread1, std::ref;t1.detach();std::cout << ft1.get() << std::endl;

std::promise

从字面意思上理解promise代表一个承诺。promise比std::packaged_task抽象层次低。
std::promise<T>提供了一种设置值的方式,它可以在这之后通过相关联的std::future<T>对象进行读取。换种说法,之前已经说过std::future可以读取一个异步函数的返回值了,那么这个std::promise就提供一种方式手动让future就绪。

#include <future>#include <string>#include <thread>#include <iostream>void print(std::promise<std::string>& p){    p.set_value("There is the result whitch you want.");}void do_some_other_things(){    std::cout << "Hello World" << std::endl;}int main(){    std::promise<std::string> promise;    std::future<std::string> result = promise.get_future();    std::thread t(print, std::ref;    do_some_other_things();    std::cout << result.get() << std::endl;    t.join();    return 0;}

由此可以看出在promise创建好的时候future也已经创建好了
线程在创建promise的同时会获得一个future,然后将promise传递给设置他的线程,当前线程则持有future,以便随时检查是否可以取值。

3,作为线程的参数时,用std::ref。把task放在线程里后,就是异步执行了。

std::packaged_task<std::string> task1(call_texi);std::future<std::string> ft1 = task1.get_future();std::thread t1(std::ref, 100);t1.detach();//task1是异步执行,也就是在新的线程里执行。std::cout << "111111111111111111111111111111" << std::endl;

3,std::promise的拷贝构造函数是被删除了的,所以std::promise作为函数的参数时,必须用std::ref.

总结

future的表现为期望,当前线程持有future时,期望从future获取到想要的结果和返回,可以把future当做异步函数的返回值。而promise是一个承诺,当线程创建了promise对象后,这个promise对象向线程承诺他必定会被人设置一个值,和promise相关联的future就是获取其返回的手段。

代码:

#include <deque>#include <mutex>#include <future>#include <thread>#include <iostream>#include <unistd.h>#include <string>//#include <utility>std::mutex mut;std::deque<std::packaged_task<std::string>> tasks;void manage_tasks(){  while{    sleep;    //std::cout << "please wait for a moument" << std::endl;    std::packaged_task<std::string> task;    {      std::lock_guard<std::mutex> lg;      if(tasks.empty continue;      std::cout << "----------------------not empty---------------" << std::endl;      task = std::move(tasks.front;      tasks.pop_front();    }    task;    //std::string s = task;  }}template<typename Call>std::future<std::string> add_task{  std::cout << "----------------------add_task---------------" << std::endl;    std::packaged_task<std::string> task;  std::future<std::string> ret = task.get_future();  std::lock_guard<std::mutex> lg;  tasks.push_back(std::move;  return ret;}std::string call_texi(int i = 0){  std::cout << "-------------jiaoche---------------" << std::endl;  if{    return "aaa";  }else{    return "bbb";  }}std::string call_zhuanche{  std::cout << "zhuanche:" << i << std::endl;  return std::to_string;}int main(){    std::thread background_thread(manage_tasks);  background_thread.detach();  std::future<std::string> fut1 = add_task(call_texi);  std::cout << fut1.get() << std::endl;    std::future<std::string> fut2 = add_task(call_zhuanche);  std::cout << fut2.get() << std::endl;  pthread_exit;}

github源代码

编译方法:

g++ -g XXX.cpp -std=c++11 -pthread

运行结果:

----------------------add_task-------------------------------------not empty----------------------------jiaoche---------------aaa----------------------add_task-------------------------------------not empty---------------zhuanche:11

代码:

#include <future>#include <thread>#include <iostream>void thread1(std::promise<int>& p, int val){  std::cout << "in thread1" << std::endl;  p.set_value;}int main(){  std::promise<int> p;  std::future<int> f = p.get_future();  std::thread t1(thread1, std::ref;  t1.detach();  std::cout << f.get() << std::endl;  pthread_exit;}

github源代码

代码分析:在队列里保存std::packaged_task,启动一个后台线程background_thread,上锁,监视队列里是否有了新的task,有了新的task,就取出来用右值赋值的方式,然后出队这个task,解锁。执行这个task。

c/c++ 学习互助QQ群:877684253

图片 1

迷惑点:

  • add_task的调用时点,是可以知道传递什么参数的,但是调用add_task时,由于语法的限制不能够把参数传递给call_zhuanche方法或者call_taxi方法,只有在manage_tasks方法里调用task方法时,才能够传递参数,可是在这个时点,参数从哪里来???求大神指点!!!

本人微信:xiaoshitou5854

c/c++ 学习互助QQ群:877684253

图片 2

本人微信:xiaoshitou5854