1. 程式人生 > >c/c++ 多執行緒 等待一次性事件 packaged_task用法

c/c++ 多執行緒 等待一次性事件 packaged_task用法

多執行緒 等待一次性事件 packaged_task用法

背景:不是很明白,不知道為了解決什麼業務場景,感覺std::asynck可以優雅的搞定一切,一次等待性事件,為什麼還有個packaged_task。

用法:和std::async一樣,也能夠返回std::future,通過呼叫get_future方法。也可以通過future得到執行緒的返回值。

特點:

1,是個模板類,模板型別是個方法型別,比如double(int),有一個引數,型別是int,返回值型別是double。

std::packaged_task<double(int)> task(func);//func是個方法,有一個引數,型別是int,返回值型別是double

2,直接執行std::packaged_task的物件task時,不是非同步執行,是在原來的執行緒上阻塞執行,也就是說,只有task執行結束後,後面的程式碼才能被執行,也就是說不是多執行緒執行。

std::packaged_task<std::string(int)> task1(call_texi);
std::future<std::string> ft1 = task1.get_future();
task1(100);//task1執行完成後,才能執行下面的列印輸出的程式碼,不是在新的執行緒裡執行task1(100)
std::cout << "111111111111111111111111111111" << std::endl;

3,作為執行緒的引數時,必須用std::move轉成右值,否則編譯不過。把task放線上程裡後,就是非同步執行了。

std::packaged_task<std::string(int)> task1(call_texi);
std::future<std::string> ft1 = task1.get_future();
std::thread t1(std::move(task1), 100);
t1.detach();//task1(100)是非同步執行,也就是在新的執行緒裡執行。
std::cout << "111111111111111111111111111111" << std::endl;

4,std::packaged_task的拷貝建構函式是被刪除了的,所以std::packaged_task作為函式的引數時,必須用std::move(task),把它轉成右值引用。

程式碼:

#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(int)>> tasks;

void manage_tasks(){
  while(true){
    sleep(1);
    //std::cout << "please wait for a moument" << std::endl;
    std::packaged_task<std::string(int)> task;
    {
      std::lock_guard<std::mutex> lg(mut);
      if(tasks.empty()) continue;
      std::cout << "----------------------not empty---------------" << std::endl;
      task = std::move(tasks.front());
      tasks.pop_front();
    }
    task(1);
    //std::string s = task(10);
  }
}

template<typename Call>
std::future<std::string> add_task(Call ca){
  std::cout << "----------------------add_task---------------" << std::endl;  
  std::packaged_task<std::string(int)> task(ca);
  std::future<std::string> ret = task.get_future();
  std::lock_guard<std::mutex> lg(mut);
  tasks.push_back(std::move(task));
  return ret;
}

std::string call_texi(int i = 0){
  std::cout << "-------------jiaoche---------------" << std::endl;
  if(i == 1){
    return "aaa";
  }else{
    return "bbb";
  }
}

std::string call_zhuanche(int i){
  std::cout << "zhuanche:" << i << std::endl;
  return std::to_string(i);
}
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(NULL);

}

github原始碼

編譯方法:

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

執行結果:

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

程式碼分析:在佇列裡儲存std::packaged_task,啟動一個後臺執行緒background_thread,上鎖,監視佇列裡是否有了新的task,有了新的task,就取出來用右值賦值的方式,然後出隊這個task,解鎖。執行這個task。

迷惑點:

  • add_task的呼叫時點,是可以知道傳遞什麼引數的,但是呼叫add_task時,由於語法的限制不能夠把引數傳遞給call_zhuanche方法或者call_taxi方法,只有在manage_tasks方法裡呼叫task方法時,才能夠傳遞引數,可是在這個時點,引數從哪裡來???求大神指點!!!

c/c++ 學習互助QQ群:877684253

本人微信:xiaoshitou5854