C++ std::async 用法與範例

本篇介紹 C++ 的 std::async 非同步函式的用法教學,並提供一些入門常用的範例程式碼。

首先介紹 std::async 的兩種模式,分為 async 與 deferred,白話一點的解釋如下:
std::launch::async:非同步模式,當下就建立執行緒去運算生成資料,當別人來跟我要資料時我可能已經運算完這些資料了(在呼叫 std::async 時建立新執行緒去執行運算)。
std::launch::deferred:拖延/懶惰模式,別人來跟我要資料時,我才去建立執行緒運算生成這些資料(在呼叫 std::async 時還不會建立新執行緒,而是呼叫 std::future 的函式時才開始建立新執行緒去運算執行)。
需要引入的標頭檔<future>

本篇內容將分為:

  • 範例1. async (async policy)
  • 範例2. async (deferred policy)
  • 範例3. async (default policy)
  • async 與 thread 的差異之處

範例1. async (async policy)

以下範例為 async policy 的示範,中譯叫非同步策略,意思是同時進行,main() 主執行緒呼叫 std::async 建構子立即返回後即繼續執行,std::async 會將這個 foo() 丟到另外的執行緒去執行,所以以下的範例輸出將會有兩種情形,一種是主執行緒很快的就印出 --1--,另一種情形是先印出 a3 再印出 --1--

std-async1.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// g++ std-async1.cpp -o a.out -std=c++11 -pthread
#include <iostream>
#include <thread>
#include <future>

using namespace std;

void foo(const std::string& str) {
std::cout << str << endl;
}

int main() {
auto a1 = std::async(std::launch::async, foo, "a1");
cout << "--1--\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
cout << "--2--\n";
a1.get();
cout << "--3--\n";
return 0;
}

輸出

1
2
3
4
a1--1--

--2--
--3--

or

1
2
3
4
--1--
a1
--2--
--3--


範例2. async (deferred policy)

以下範例是 async 使用 deferred policy 這個策略,什麼是deferred policy 呢?deferred policy 就是拖延/懶惰模式,簡單說就是別人來跟我要資料時,我才去運算生產/運算這些資料,這裡的 wait() 或者 get() 就是別人來跟我要資料的比喻。
當 a2.get() or a2.wait() 被呼叫時,才去同步執行 foo() 印出 “a2”。
future 的結果有三種方式:
get():等待非同步執行結束並回傳結果。
wait():等待非同步執行結束,沒有回傳值。
wait_for():超過等待時間回傳結果。

std-async2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// g++ std-async2.cpp -o a.out -std=c++11 -pthread
#include <iostream>
#include <thread>
#include <future>

using namespace std;

void foo(const std::string& str) {
std::cout << str << endl;
}

int main() {
auto a2 = std::async(std::launch::deferred, foo, "a2");
cout << "--1--\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
cout << "--2--\n";
a2.wait(); // 印出 a2
cout << "--3--\n";
return 0;
}

輸出

1
2
3
4
--1--
--2--
a2
--3--

這個功能在實作爬蟲時特別好用,例如:一次同時爬 n 個網站。


範例3. async (default policy)

以下為 default policy 的範例,default policy 就是 async policy 或 deferred policy 兩種其中一種,基本上 default policy 就是前兩種範例的混合體囉!所以在輸出的結果上也會有兩種情形。

std-async3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// g++ std-async3.cpp -o a.out -std=c++11 -pthread
#include <iostream>
#include <thread>
#include <future>

using namespace std;

void foo(const std::string& str) {
std::cout << str << endl;
}

int main() {
auto a3 = std::async(foo, "a3");
cout << "--1--\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
cout << "--2--\n";
cout << "--3--\n";
return 0;
}

輸出

1
2
3
4
a3--1--

--2--
--3--

or

1
2
3
4
--1--
a3
--2--
--3--

async 與 thread 的差異之處

要取得運算結果時,使用 thread 要取得運算結果通常是在全域變數宣告一個變數,之後使用 join() 再將得到這個變數值,過程比較繁瑣些,使用 async 的話,可以透過回傳的 future 去取得這個運算結果,非常方便。

接下來下篇會介紹實際上什麼情況會需要用到 async 與應用場合及範例。


參考
[1] std::async - cppreference.com - C++ Reference
https://en.cppreference.com/w/cpp/thread/async
[2] async - C++ Reference
http://www.cplusplus.com/reference/future/async/
[3] C++11 程式的平行化:async 與 future
https://kheresy.wordpress.com/2016/03/14/async-and-future-in-c11/
[4] C++ 使用 Async 非同步函數開發平行化計算程式教學
https://blog.gtwang.org/programming/cpp-11-async-function-parallel-computing-tutorial/
[5] C++並發實戰13:std::future、std::async、std::promise、std::packaged_task - wanghualin033的博客 - CSDN博客
https://blog.csdn.net/wanghualin033/article/details/89421221
[6] Futures with std::async
http://jakascorner.com/blog/2016/02/futures.html
[7] std::async 的两个坑 - 知乎
https://zhuanlan.zhihu.com/p/39757902
在 std::future 解構時會 block 直到非同步的執行結束,所以用 lambda expression 時需注意。


相關文章
C/C++ 新手入門教學懶人包
std::thread 用法與範例