欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++ std::async的使用總結(jié)

 更新時間:2021年01月27日 08:31:22   作者:VE視頻引擎  
這篇文章主要介紹了C++ std::async的使用總結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

C++98 標(biāo)準(zhǔn)中并沒有線程庫的存在,直到 C++11 中才終于提供了多線程的標(biāo)準(zhǔn)庫,提供了管理線程、保護(hù)共享數(shù)據(jù)、線程間同步操作、原子操作等類。多線程庫對應(yīng)的頭文件是 #include <thread> ,類名為 std::thread 。

然而線程畢竟是比較貼近系統(tǒng)的東西,使用起來仍然不是很方便,特別是線程同步及獲取線程運(yùn)行結(jié)果上就更加麻煩。我們不能簡單的通過 thread.join() 得到結(jié)果,必須定義一個線程共享的變量來傳遞結(jié)果,同時還要考慮線程間的互斥問題。好在 C++11 中提供了一個相對簡單的異步接口 std::async ,通過這個接口可以簡單的創(chuàng)建線程并通過 std::future 中獲取結(jié)果。以往都是自己去封裝線程實(shí)現(xiàn)自己的async,現(xiàn)在有線程的跨平臺接口可以使用就極大的方便了C++多線程編程。

先看一下 std::async 的函數(shù)原型

//(C++11 起) (C++17 前)
template< class Function, class... Args>
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
  async( Function&& f, Args&&... args );

//(C++11 起) (C++17 前)
template< class Function, class... Args >
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
  async( std::launch policy, Function&& f, Args&&... args );

第一個參數(shù)是線程的創(chuàng)建策略,有兩種策略可供選擇:

  • std::launch::async:在調(diào)用async就開始創(chuàng)建線程。
  • std::launch::deferred:延遲加載方式創(chuàng)建線程。調(diào)用async時不創(chuàng)建線程,直到調(diào)用了future的get或者wait時才創(chuàng)建線程。

默認(rèn)策略是: std::launch::async | std::launch::deferred 也就是兩種策略的合集,具體什么意思后面詳細(xì)再說

第二個參數(shù)是線程函數(shù)
線程函數(shù)可接受 function, lambda expression, bind expression, or another function object

第三個參數(shù)是線程函數(shù)的參數(shù)
不再說明

返回值std::future
std::future 是一個模板類,它提供了一種訪問異步操作結(jié)果的機(jī)制。從字面意思上看它表示未來,這個意思就非常貼切,因?yàn)樗皇橇⒓传@取結(jié)果但是可以在某個時候以同步的方式來獲取結(jié)果。我們可以通過查詢future的狀態(tài)來獲取異步操作的結(jié)構(gòu)。future_status有三種狀態(tài):

  • deferred:異步操作還未開始
  • ready:異步操作已經(jīng)完成
  • timeout:異步操作超時,主要用于std::future .wait_for()

示例:

//查詢future的狀態(tài)
std::future_status status;
do {
  status = future.wait_for(std::chrono::seconds(1));
  if (status == std::future_status::deferred) {
    std::cout << "deferred" << std::endl;
  } else if (status == std::future_status::timeout) {
    std::cout << "timeout" << std::endl;
  } else if (status == std::future_status::ready) {
    std::cout << "ready!" << std::endl;
  }
} while (status != std::future_status::ready);

std::future 獲取結(jié)果的方式有三種:

  • get:等待異步操作結(jié)束并返回結(jié)果
  • wait:等待異步操作結(jié)束,但沒有返回值
  • waite_for:超時等待返回結(jié)果,上面示例中就是對超時等待的使用展示

介紹完了 std::async 的函數(shù)原型,那么它到底該如何使用呢?

std::async 的基本用法: 示例鏈接

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <future>
#include <string>
#include <mutex>

std::mutex m;
struct X {
  void foo(int i, const std::string& str) {
    std::lock_guard<std::mutex> lk(m);
    std::cout << str << ' ' << i << '\n';
  }
  void bar(const std::string& str) {
    std::lock_guard<std::mutex> lk(m);
    std::cout << str << '\n';
  }
  int operator()(int i) {
    std::lock_guard<std::mutex> lk(m);
    std::cout << i << '\n';
    return i + 10;
  }};

template <typename RandomIt>int parallel_sum(RandomIt beg, RandomIt end){
  auto len = end - beg;
  if (len < 1000)
    return std::accumulate(beg, end, 0);

  RandomIt mid = beg + len/2;
  auto handle = std::async(std::launch::async,
               parallel_sum<RandomIt>, mid, end);
  int sum = parallel_sum(beg, mid);
  return sum + handle.get();
}

int main(){
  std::vector<int> v(10000, 1);
  std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n';

  X x;
  // 以默認(rèn)策略調(diào)用 x.foo(42, "Hello") :
  // 可能同時打印 "Hello 42" 或延遲執(zhí)行
  auto a1 = std::async(&X::foo, &x, 42, "Hello");
  // 以 deferred 策略調(diào)用 x.bar("world!")
  // 調(diào)用 a2.get() 或 a2.wait() 時打印 "world!"
  auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!");
  // 以 async 策略調(diào)用 X()(43) :
  // 同時打印 "43"
  auto a3 = std::async(std::launch::async, X(), 43);
  a2.wait();           // 打印 "world!"
  std::cout << a3.get() << '\n'; // 打印 "53"
} // 若 a1 在此點(diǎn)未完成,則 a1 的析構(gòu)函數(shù)在此打印 "Hello 42"

可能的結(jié)果

The sum is 10000
43
world!
53
Hello 42

由此可見, std::async 是異步操作做了一個很好的封裝,使我們不用關(guān)注線程創(chuàng)建內(nèi)部細(xì)節(jié),就能方便的獲取異步執(zhí)行狀態(tài)和結(jié)果,還可以指定線程創(chuàng)建策略。

深入理解線程創(chuàng)建策略

  • std::launch::async調(diào)度策略意味著函數(shù)必須異步執(zhí)行,即在另一線程執(zhí)行。
  • std::launch::deferred調(diào)度策略意味著函數(shù)可能只會在std::async返回的future對象調(diào)用get或wait時執(zhí)行。那就是,執(zhí)行會推遲到其中一個調(diào)用發(fā)生。當(dāng)調(diào)用get或wait時,函數(shù)會同步執(zhí)行,即調(diào)用者會阻塞直到函數(shù)運(yùn)行結(jié)束。如果get或wait沒有被調(diào)用,函數(shù)就絕對不會執(zhí)行。

兩者策略都很明確,然而該函數(shù)的默認(rèn)策略卻很有趣,它不是你顯示指定的,也就是第一個函數(shù)原型中所用的策略即 std::launch::async | std::launch::deferred ,c++標(biāo)準(zhǔn)中給出的說明是:

進(jìn)行異步執(zhí)行還是惰性求值取決于實(shí)現(xiàn)

auto future = std::async(func);    // 使用默認(rèn)發(fā)射模式執(zhí)行func

這種調(diào)度策略我們沒有辦法預(yù)知函數(shù)func是否會在哪個線程執(zhí)行,甚至無法預(yù)知會不會被執(zhí)行,因?yàn)閒unc可能會被調(diào)度為推遲執(zhí)行,即調(diào)用get或wait的時候執(zhí)行,而get或wait是否會被執(zhí)行或者在哪個線程執(zhí)行都無法預(yù)知。

同時這種調(diào)度策略的靈活性還會混淆使用thread_local變量,這意味著如果func寫或讀這種線程本地存儲(Thread Local Storage,TLS),預(yù)知取到哪個線程的本地變量是不可能的。

它也影響了基于wait循環(huán)中的超時情況,因?yàn)檎{(diào)度策略可能為 deferred 的,調(diào)用wait_for或者wait_until會返回值std::launch::deferred。這意味著下面的循環(huán),看起來最終會停止,但是,實(shí)際上可能會一直運(yùn)行:

void func()      // f睡眠1秒后返回
{
  std::this_thread::sleep_for(1);
}
auto future = std::async(func);   // (概念上)異步執(zhí)行f
while(fut.wait_for(100ms) !=     // 循環(huán)直到f執(zhí)行結(jié)束
   std::future_status::ready)   // 但這可能永遠(yuǎn)不會發(fā)生
{
  ...
}

為避免陷入死循環(huán),我們必須檢查future是否把任務(wù)推遲,然而future無法獲知任務(wù)是否被推遲,一個好的技巧就是通過wait_for(0)來獲取future_status是否是deferred:

auto future = std::async(func);   // (概念上)異步執(zhí)行f
if (fut.wait_for(0) == std::future_status::deferred) // 如果任務(wù)被推遲
{
  ...   // fut使用get或wait來同步調(diào)用f
} else {      // 任務(wù)沒有被推遲
  while(fut.wait_for(100ms) != std::future_status::ready) { // 不可能無限循環(huán)
   ...  // 任務(wù)沒有被推遲也沒有就緒,所以做一些并發(fā)的事情直到任務(wù)就緒
  }
  ...    // fut就緒
}

有人可能會說既然有這么多缺點(diǎn)為啥還要用它,因?yàn)楫吘刮覀兛紤]的極限情況下的可能,有時候我不要求它是并發(fā)還是同步執(zhí)行,也不需要考慮修改那個線程thread_local變量,同時也能接受可能任務(wù)永遠(yuǎn)不會執(zhí)行,那么這種方式就是一種方便且高效的調(diào)度策略。

綜上所述,我們總結(jié)出以下幾點(diǎn):

  • std::async的默認(rèn)調(diào)度策略既允許任務(wù)異步執(zhí)行,又允許任務(wù)同步執(zhí)行。
  • 默認(rèn)策略靈活性導(dǎo)致了使用thread_local變量時的不確定性,它隱含著任務(wù)可能不會執(zhí)行,它還影響了基于超時的wait調(diào)用的程序邏輯。
  • 如果異步執(zhí)行是必需的,指定std::launch::async發(fā)射策略。

到此這篇關(guān)于C++ std::async的使用總結(jié)的文章就介紹到這了,更多相關(guān)C++ std::async內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言深入講解動態(tài)內(nèi)存分配函數(shù)的使用

    C語言深入講解動態(tài)內(nèi)存分配函數(shù)的使用

    這篇文章主要介紹了C語言動態(tài)內(nèi)存分配,C語言內(nèi)存管理相關(guān)的函數(shù)主要有realloc、calloc、malloc、free、柔性數(shù)組等,下面這篇文章帶大家了解一下
    2022-05-05
  • C語言FlappyBird飛揚(yáng)的小鳥實(shí)現(xiàn)開發(fā)流程

    C語言FlappyBird飛揚(yáng)的小鳥實(shí)現(xiàn)開發(fā)流程

    因?yàn)樵诩艺撕枚嗵?,隨手玩了下自己以前做的一些小游戲,說真的,有幾個游戲做的是真的劣質(zhì),譬如 flappybird 真的讓我難以忍受,于是重做了一波分享給大家
    2022-11-11
  • C語言編程const遇上指針分析

    C語言編程const遇上指針分析

    本篇文章是C語言編程篇,主要為大家介紹C語言編程中當(dāng)Const遇上指針的分析講解,有需要的朋友可以借鑒參考下,希望可以有所幫助
    2021-09-09
  • 解析sizeof, strlen, 指針以及數(shù)組作為函數(shù)參數(shù)的應(yīng)用

    解析sizeof, strlen, 指針以及數(shù)組作為函數(shù)參數(shù)的應(yīng)用

    本篇文章是對sizeof, strlen, 指針以及數(shù)組作為函數(shù)參數(shù)的應(yīng)用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • 最新評論