天天看點

[modern c++] std::future 與 std::async / std::promise / std::packaged_task 一起使用

描述:

std::future 可以用來擷取所有異步操作的結果,一般情況下 std::future 都會和 std::async , std::promise 或 std::packaged_task 一起使用。

std::future 有一個 valid 狀态,當且僅當其valid狀态時true時,才可以使用它,std::async , std::promise 或 std::packaged_task 建立的 std::future 都是 valid == true的 ,如果自行使用 std::future 的構造函數建立一個執行個體,那麼它一定不是 valid 的,可以把 valid 狀态當做一個 std::future 是否和某個異步任務相關聯的标志,valid == false 代表目前 std::future 實際沒有關聯任何異步任務。

因為其内部已經有關聯的異步任務了,可以把valid看作是std::future是否初始化完成的标志,一般初始化都比較快,是以正常情況下invalid狀态很少出現,一般隻在沒指定異步任務時出現。另外如果使用std::move把異步任務從某個std::future轉移給另一個,那麼之前那個std::future會變成invalid狀态。

std::future 有兩套阻塞等待函數 waitX() 和 get(), waitX()會在任務沒有執行完畢的情況下一直阻塞或者阻塞等待一段時間,但是不會改變std::future的valid狀态。

get() 會阻塞等待任務執行完畢并且傳回任務的傳回值,同時會把 std::future 的狀态變成 invalid 狀态。這個時候隻有重新做 move 或者重新通過 std::async , std::promise 或 std::packaged_task 才能再次激活 std::future。

換句話說,每個std::future 隻能 get 一次,重複 get 會導緻抛異常,進而崩潰。

鑒于不能重複 get 的問題,很多情況下我們隻和 void 傳回值的可調用對象一起使用,然後不去調用get函數,隻用wait作同步隻用。

與 std::async 一起使用

#include <future>
#include <string>
#include <mutex>
#include <stdio.h>
#include <unistd.h>
#include <thread>
#include <iostream>
 

std::atomic<bool> bbb{false};

int func()
{
  while(!bbb){
    sleep(1);
  }
  std::cout << "func stop" << std::endl;
  
  return 99;
}
 
int main()
{
  std::future<int> ret;
  
  //沒有關聯任何任務,是以是invalid
  if(ret.valid()){            
    std::cout << "valid" << std::endl;
  }else{
    std::cout << "invalid" << std::endl;
  }
  
  std::cout << "----------" << std::endl;
  
  std::future<int> ret1 = std::async<int()>(std::launch::async,func);
  
  if(ret1.valid()){
    std::cout << "before move ret1 : valid" << std::endl;
  }else{
    std::cout << "before move  ret1 : invalid" << std::endl;
  }
  
  ret = std::move(ret1);
  
  if(ret1.valid()){
    std::cout << "after move ret1 : valid" << std::endl;
  }else{
    std::cout << "after : invalid" << std::endl;
  }
  
  std::cout << "----------" << std::endl;
  
  //關聯任務,是以是valid
  if(ret.valid()){
    std::cout << "valid" << std::endl;
  }else{
    std::cout << "invalid" << std::endl;
  }
  
  std::cout << "----------" << std::endl;
  
  //把任務停掉,仍然是valid
  bbb = true;
  //等待可執行對象執行完畢,如果在wait之前已經執行完畢,那麼這裡直接傳回
  ret.wait();
  
  //wait 隻是等待異步任務結束,不會重置目前std::future,是以還是valid
  if(ret.valid()){
    std::cout << "valid" << std::endl;
  }else{
    std::cout << "invalid" << std::endl;
  }
  
  std::cout << "----------" << std::endl;
  
  //get 會等待異步任務結束并擷取結果,get會重置 std::future,是以會變成 invalid
  int result = ret.get();
  
  if(ret.valid()){
    std::cout << "valid" << std::endl;
  }else{
    std::cout << "invalid" << std::endl;
  }
  
}      

與 std::promise一起使用

與 std::packaged_task 一起使用

繼續閱讀