並行模式庫PPL應用實戰(一):使用task類創建並行任務
好了,從最簡單的代碼開始,先演示下如何使用 task 類和 lambda 表達式創建一個並行任務:
// final_answer.cpp// compile with: /EHsc #include <ppltasks.h>#include <iostream>using namespace concurrency;using namespace std;int main(int argc, char *argv[]){ task<int> final_answer([] { return 42; }); cout << "The final answer is: " << final_answer.get() << endl; return 0;}
使用 Visual Studio 命令行工具編譯
cl /EHsc final_answer.cpp
執行結果為:
The final answer is: 42
task 類的原型如下:
template<typename _ReturnType>class task;
其模板參數 _ReturnType 是任務返回值類型。 task:get 方法則用於獲取返回值,原型如下:
_ReturnType get() const;
task 類的構造函數原型:
template<typename T>__declspec(noinline) explicit task(T _Param);
可以看到這是個模板函數,其參數 _Param 可以是 lambda 表達式、函數對象、仿函數、函數指針等可以以
_Param()
形式調用的類型,或者 PPL 中的 task_completion_event<result_type> 類型。因此可以使用各種靈活的方式構造 task 對象,其中 lambda 表達式無疑是最方便常用的一種。
接下來我們修改上面的程序,列印出線程 id 以便觀察並行任務的執行情況。
// final_answer_1.cpp// compile with: /EHsc #include <ppltasks.h>#include <iostream>#include <thread>using namespace concurrency;using namespace std;int main(int argc, char *argv[]){ cout << "Major thread id is: " << this_thread::get_id() << endl; task<int> final_answer([] { cout << "Thread id in task is:" << this_thread::get_id() << endl; return 42; }); cout << "The final answer is: " << final_answer.get() << endl; return 0;}
繼續編譯執行,得到輸出結果:
Major thread id is: 164824
Thread id in task is: 164824
The final answer is: 42
注意兩個線程 id 是相同的,很有些意外,任務是在主線程執行的而非預計的其他後台工作線程。實際上這是 PPL 的優化策略造成的。
再修改下程序,在 task 對象構造完成後加一個 sleep 調用掛起當前線程一小段時間:
int main(int argc, char *argv[]){ cout << "Major thread id is: " << this_thread::get_id() << endl; task<int> final_answer([] { cout << "Thread id in task is:" << this_thread::get_id() << endl; return 42; }); this_thread::sleep_for(chrono::milliseconds(1)); cout << "The final answer is: " << final_answer.get() << endl; return 0;}
這次輸出結果發生了變化:
Major thread id is: 173404
Thread id in task is: 185936
The final answer is: 42
PPL 使用了一個新的線程執行並行任務,實際上 PPL 是使用了線程池來執行被調度到的任務。
而在上一個程序中,由於沒有 sleep,也沒有其他耗時的代碼,執行到 task::get 方法時並行任務尚未被調度所以直接在當前線程執行該任務,這樣就節省了兩次線程切換的開銷。
MSDN 中的說明:
It is possible for wait to execute the task inline, if all of the tasks dependencies are satisfied, and it has not already been picked up for execution by a background worker.
task::get 方法的內部實現會先調用 task::wait 方法所以有同樣的效果。
本章小結:
1. task 類對象構造完成後即可被調度執行;
2. 並行有可能被優化在當前線程執行;
留一個問題,如果 task 對象構造後馬上析構,該並行任務是否會被調度執行呢?
本章代碼使用 visual studio community 2013 編譯調試通過。
本章參考文檔:
How to: Create a Task that Completes After a Delay task Class (Concurrency Runtime)推薦閱讀:
※軟體項目開發,全系列規範及約束文件
※輕鬆理解UML用例圖時序圖類圖的教程
※Windows 10原生應用將迎來獨立Insider預覽項目