C std async的使用總結

2022-09-25 05:03:09 字數 3212 閱讀 8962

c++98 標準中並沒有執行緒庫的存在,直到 c++11 中才終於提供了多執行緒的標準庫,提供了管理執行緒、保護共享資料、執行緒間同步操作、原子操作等類。多執行緒庫對應的標頭檔案是 #include ,類名為 std::thread 。

然而執行緒畢竟是比較貼近系統的東西,使用起來仍然不是很方便,特別是執行緒同步及獲取執行緒執行結果上就更加麻煩。我們不能簡單的通過 thread.join() 得到結果,必須定義乙個執行緒共享的變數來傳遞結果,同時還要考慮執行緒間的互斥問題。好在 c++11 中提供了乙個相對簡單的非同步介面 std::async ,通過這個介面可以簡單的建立執行緒並通過 std::future 中獲取結果。以往都是自己去封裝執行緒實現自己的async,現在有執行緒的跨平台介面可以使用就極大的方便了c++多執行緒程式設計。

先看一下 std::async 的函式原型

//(c++11 起) (c++17 前)

template< class function, class... args>

std::future<:result_of_t>(std::decay_t...)>>

async( function&& f, args&&... args );

//(c++11 起) (c++17 前)

template< class function, class... args >

std::future<:result_of_t>(std::decay_t...)>>

async( std::launch policy, function&& f, args&&... args );

第乙個引數是執行緒的建立策略,有兩種策略可供選擇:

預設策略是:std::launch::async | std::launch::deferred也就是兩種策略的合集,具體什麼意思後面詳細再說

第二個引數是執行緒函式

執行緒函式可接受function, lambda expression, bind expression, or another function object

第三個引數是執行緒函式的引數

不再說明

返回值std::future

std::future 是乙個模板類,它提供了一種訪問非同步操作結果的機制。從字面意思上看它表示未來,這個意思就非常貼切,因為她不是立即獲取結果但是可以在某個時候以同步的方式來獲取結果。我們可以通過查詢future的狀態來獲取非同步操作的結構。future_status有三種狀態:

示例://查詢future的狀態

std::future_status status;

do else if (status == std::future_status::timeout) else if (status == std::future_status::ready)

} while (status != std::future_status::ready);

std::future 獲取結果的方式有三種:

介紹完了 std::async 的函式原型,那麼它到底該如何使用呢?

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

#include

#include

#include

#include

#include

#include

#include

std::mutex m;

struct x

void bar(const std::string& str)

int operator()(int i) };

template int parallel_sum(randomit beg, randomit end)

int main() // 若 a1 在此點未完成,則 a1 的析構函式在此列印 "hello 42"

可能的結果

the sum is 10000

43world!

53hello 42

由此可見, std::async 是非同步操作做了乙個很好的封裝,使我們不用關注執行緒建立內部細節,就能方便的獲取非同步執行狀態和結果,還可以指定執行緒建立策略。

深入理解執行緒建立策略

兩者策略都很明確,然而該函式的預設策略卻很有趣,它不是你顯示指定的,也就是第乙個函式原型中所用的策略即std::launch::async | s程式設計客棧td::launch::deferred,c++標準中給出的說明是:

進行非同步執行還是惰性求值取決於實現

auto future = std::async(func); // 使用預設發射模式執行func

這種排程策略我們沒有辦法預知函式func是否會在哪個執行緒執行,甚至無法預知會不會被執行,因為func可能會被排程為推遲執行,即呼叫get或wait的時候執行,而get或wait是否會被執行或者在哪個執行緒執行都無法預知。

同時這種排程策略的靈活性還會混淆使用thread_local變數這意味著如果func寫或讀這種執行緒本地儲存(thread local storage,tls),預知取到哪個執行緒的本地變數是不可能的。

它也影響了基於wait迴圈中的超時情況,因為排程策略可能為 deferred 的,呼叫wait_f程式設計客棧or或者wait_until會返回值std::launch::deferred。這意味著下面的迴圈,看起來最終會停止,但是,實際上可能會一直執行:

void func() // f睡眠1秒後返回

auto future = std::async(func); // (概念上)非同步執行f

while(fut.wait_for(100ms) != // 迴圈直到f執行結束

std::future_status::ready) // 但這可能永遠不會發生

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

auto future = std::async(func); // (概念上)非同步執行f

if (fut.wait_for(0) == std::future_status::deferred) // 如果任務被推遲

else

... // fut就緒

}有人可能會說既然有這麼多缺點為啥還要用它,因為畢竟我們考慮的極限情況下的可能,有時候我不要求它是併發還是同步執行,也不需要考慮修改那個執行緒thread_local變數,同時也能接受可能任務永遠不會執行,那麼這種方式就是一種方便且高效的排程策略。

綜上所述,我們總結出以下幾點:

vi 的使用總結

用vi 用的久了,總感覺每次程式設計都要自己在table鍵進行縮排很是不爽,於是總結了一點vi使用小技巧 對vim 編輯器的屬性進行定製可以方便 的編寫。vim 的配置檔案為 etc vimrc,如果在 etc下沒有這個目錄的話,可以進行查詢,在vim底行模式下輸入 scriptnames就會找到v...

C sizeof的使用總結

說明 以下 在 vs2008 中通過,在 32位作業系統下。1.定義 sizeof 是乙個操作符 operator 其作用是返回乙個物件或型別所佔的記憶體位元組數。其返回值型別為 size t size t 在標頭檔案 stddef.h 中定義,它依賴於編譯系統的值,一般定義為 typedef un...

DataReader的使用總結

對學習.net的人來說ado.net應當是不陌生了,不過我在這裡還是想總結一下關於datareader的使用 datareader包括了兩種形式 sqldatareader和oledbdatareader,其中很多的用法和屬性都是相通的。首先datareader從資料庫當中檢索唯讀,只進的資料流,它...