亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

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

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

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

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

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

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

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

  • deferred:異步操作還未開始
  • ready:異步操作已經(jīng)完成
  • timeout:異步操作超時(shí),主要用于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:超時(shí)等待返回結(jié)果,上面示例中就是對(duì)超時(shí)等待的使用展示

介紹完了 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") :
  // 可能同時(shí)打印 "Hello 42" 或延遲執(zhí)行
  auto a1 = std::async(&X::foo, &x, 42, "Hello");
  // 以 deferred 策略調(diào)用 x.bar("world!")
  // 調(diào)用 a2.get() 或 a2.wait() 時(shí)打印 "world!"
  auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!");
  // 以 async 策略調(diào)用 X()(43) :
  // 同時(shí)打印 "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 是異步操作做了一個(gè)很好的封裝,使我們不用關(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ù)可能只會(huì)在std::async返回的future對(duì)象調(diào)用get或wait時(shí)執(zhí)行。那就是,執(zhí)行會(huì)推遲到其中一個(gè)調(diào)用發(fā)生。當(dāng)調(diào)用get或wait時(shí),函數(shù)會(huì)同步執(zhí)行,即調(diào)用者會(huì)阻塞直到函數(shù)運(yùn)行結(jié)束。如果get或wait沒有被調(diào)用,函數(shù)就絕對(duì)不會(huì)執(zhí)行。

兩者策略都很明確,然而該函數(shù)的默認(rèn)策略卻很有趣,它不是你顯示指定的,也就是第一個(gè)函數(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是否會(huì)在哪個(gè)線程執(zhí)行,甚至無法預(yù)知會(huì)不會(huì)被執(zhí)行,因?yàn)閒unc可能會(huì)被調(diào)度為推遲執(zhí)行,即調(diào)用get或wait的時(shí)候執(zhí)行,而get或wait是否會(huì)被執(zhí)行或者在哪個(gè)線程執(zhí)行都無法預(yù)知。

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

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

為避免陷入死循環(huán),我們必須檢查future是否把任務(wù)推遲,然而future無法獲知任務(wù)是否被推遲,一個(gè)好的技巧就是通過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就緒
}

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

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

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

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

相關(guān)文章

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

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

    這篇文章主要介紹了C語言動(dòng)態(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)樵诩艺撕枚嗵?,隨手玩了下自己以前做的一些小游戲,說真的,有幾個(gè)游戲做的是真的劣質(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)用

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