C++多工程式設計簡明教程(1)-C++的多工其實很簡單
C++多工程式設計簡明教程 (1) – C++的多工其實很簡單
用庫的方式無法實現徹底的執行緒安全!我們需要C++11
與很多同學交流的時候發現,一想到用C++寫多執行緒,還是想到pthread這樣的庫的方法實現。
但是,十幾年前的研究就證明了,執行緒安全是無法用庫的形勢來提供的,有興趣的同學可以參見原文:
http://www.hpl.hp.com/techreports/2004/HPL-2004-209.pdf
解釋需要大量的篇幅,作為快餐式的教程,我們只講結論。
十幾年過去了,CPU的亂序執行,編譯器的亂序優化,使得通過pthread一樣的庫實現執行緒安全越來越困難。即使做到了,也是以犧牲效能為代價換來的。
我們以java為例,雖然在java 1.0中就支援多執行緒了。但是,真正完整的執行緒安全的基礎設施的完成是在java 5.0版本中才實現的。這其中最重要的一點,就是java 5.0明確定義了記憶體模型。有了記憶體模型的保證,我們才真正知道,什麼是可行的,什麼是不可行的。如何定義才能在多執行緒中實在可見性。
在有較大影響的語言中,java是率先達到了這一目標的。C++在當時基本上沒有為多執行緒或者多工做過考慮,這個問題直到C++11中才被解決掉。
所以,至少對於多執行緒程式設計來講,C++11是必須要學習的。我們不能再停留在C++98/03的老黃曆上了!
所幸的是,對於最基本的C++11多工程式設計來講,比起完整學會pthread這樣的庫還要容易.
多工優於多執行緒
然後,我們從C++11的執行緒開始講?不,我寧願把整個記憶體模型講完了之後才教您如何使用執行緒,就怕學完了執行緒沒學記憶體模型就上手直接開始用了,導致遇到問題之後才知道記憶體模型的重要性。
但是,這還不是我最想講的,為什麼要學執行緒和記憶體模型呢?大部分的任務,我們根本不需要了解什麼是執行緒,什麼是鎖,更不用說無鎖程式設計之類的了。看過我的《Java多執行緒程式設計簡明教程(1) – Future模式與AsyncTask》都知道,如果有更簡單、更高階的封裝,我是不建議先學更低階的工具的。
為什麼需要多執行緒?其實很多同學沒有意識到,需要的只是多工,執行緒只是手段。執行緒是作業系統級的概念,是作業系統級的資源,我們要管理到這麼細節,就得去處理比如管理執行緒狀態,處理執行緒數滿了的異常,如何做執行緒的負載均衡之類的管理工作,而這些根業務邏輯之間沒有半毛錢關係。
從java的發展來看,5.0提供了Future模式,7.0中提供了Fork-Join模式,封裝得越來越高階。C++11中暫時還沒有到Fork-Join這麼高階,不過,直接呼叫一個std::async函式就可以實現Future模式.
所謂的Future模式,就是在後臺起一個任務,然後前臺該幹嘛幹嘛,等到前臺任務需要用到剛才的後臺任務的時候,再去獲取後臺任務的結果。如果後臺任務已經結束了,自然是最好,繼續幹活就是了。即使後臺還沒做完,至少也能少等一會兒。不管怎麼樣都不賠。
std::async 快餐
C++11如何去做一個後臺任務呢,實在太簡單了,寫個函式,或者仿函式,或者是lambda表示式這樣可以被呼叫的邏輯,然後通過std:async去呼叫就是了。
唯一需要注意的一點,future模式不允許共享記憶體,複製一份只讀的通過引數傳給你的函式吧。
太簡單了,一共就只需要4步:
- 引入標頭檔案
- 寫需要後臺執行功能的函式
- 通過async函式呼叫上面寫的函式
- 主任務繼續幹活
- 需要後臺任務的結果時,去讀它的返回值
引入future標頭檔案
#include <future>
實現功能的函式
將後臺要做事兒的邏輯寫成一個函式吧,這裡寫個最簡單的:
void func1(int arg){
cout<<"I am running in background!"<<arg<<endl;
}
通過async呼叫您的邏輯
future<void> f1 = async(launch::async,func1,0);
大家可以看到,async有多麼的簡單,第一個引數是馬上就啟動後臺任務,還是用的時候再啟動。我們一般都是希望馬上啟動,於是固定地給launch::async就好了。真的有特殊需求要用的時候再啟動,就給launch::deferred。只有這兩種情況,簡單吧?
第二個引數就是剛才寫好的後臺邏輯的函式。第三個是您的函式要用的引數。
返回一個future,型別與剛才您寫的函式的返回值一致。這時候不需要知道它是什麼。
甚至有個更絕情的辦法,我們根本不care返回值是什麼,直接給個auto讓編譯器自己推斷去。
像下面這樣:
auto f1 = async(launch::async,func1,0);
不需要懂任何跟執行緒相關的知識吧?
主任務繼續幹活
這個就不多說了,既然要起後臺任務,前臺肯定有事兒要做。
獲取後臺的值
到了需要返回值的時候,直接呼叫get函式。本例中是void型別,所以返回值獲取了我們也不用。
f1.get();
OK,我們的快餐教程就講完了,不需要懂什麼是執行緒,什麼是鎖,什麼是記憶體模型,原子變數是什麼,記憶體都有什麼順序之類的。大家可以快樂地去幹活去了~ 等將來我們詳詳細細地把剛才列舉的這些知識一一補齊,大家就會發現這其實是有多複雜了。
例子
Java歷史上借鑑了太多的C++的東西,但是記憶體模型和Future模式這些,Java是領先於C++的。
那我們向Java致敬吧,將Java多執行緒簡明教程中Future模式的例子用C++改寫一下。我們看看C++的優勢吧:
public class AsyncTaskSimple {
public static class Result implements Callable<String>{
@Override
public String call() throws Exception {
return doRealLogic();
}
private String doRealLogic(){
//Here to do the background logic
return new String("Done");
}
}
public static void main(String[] args) {
FutureTask<String> future = new FutureTask<String>(new Result());
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.submit(future);
someThingToDo();
try {
String s = future.get();
System.out.println("The result is:"+s);
}catch (InterruptedException e){
//Deal with InterruptedExcpeiotn
}catch(ExecutionException ee){
//Deal with ExecutionException
}
}
private static void someThingToDo(){
//Main thread logic
}
}
Java寫了這麼一大坨,C++11只用不到一半的篇幅就搞定了:
string doRealLogic(){
return string("Done");
}
void someThingToDo(){
//do something
}
int main(int argc, char** argv)
{
auto f2 = async(launch::async,doRealLogic);
someThingToDo();
cout<<f2.get()<<endl;
}
或者我們更現代一點,也不要寫函式了,直接上一個lambda表示式吧。
void someThingToDo(){
//do something
}
int main(int argc, char** argv)
{
auto f2 = async(launch::async,[](){
return string("Done");
});
someThingToDo();
cout<<f2.get()<<endl;
}
最後再強調一遍,輸出引數通過函式引數傳進去,輸出引數通過返回值,也就是future.get獲取回來。不要使用全域性變數等方式來共享記憶體!
Enjoy it!
相關文章
- C++ - 簡單工廠模式C++模式
- 剖析C++多型:用C實現簡單多型C++多型
- c++簡單程式設計-3C++程式設計
- Promise 其實很簡單Promise
- 我所理解的設計模式(C++實現)——簡單工廠模式(Simple Factory Pattern)設計模式C++
- 無廢話設計模式(1)--簡單工廠、工廠方法、抽象工廠設計模式抽象
- 設計模式----簡單工廠設計模式
- 其實泛型很簡單泛型
- 用C++編寫一個簡單的員工工資管理系統~C++
- C# 設計模式(1)——簡單工廠模式、工廠模式、抽象工廠模式C#設計模式抽象
- 設計模式 - 簡單工廠模式設計模式
- 【設計模式】簡單工廠模式設計模式
- 設計模式——簡單工廠模式設計模式
- 設計模式(一):簡單工廠設計模式
- iOS設計模式 - 簡單工廠iOS設計模式
- 設計模式-簡單工廠模式設計模式
- .NET多執行緒程式設計(1):多工和多執行緒 (轉)執行緒程式設計
- 1、簡單工廠模式實現計算器功能模式
- 設計模式-簡單工廠、工廠方法模式、抽象工廠模式設計模式抽象
- Java設計模式(一) 簡單工廠模式不簡單Java設計模式
- Java中的簡單工廠設計模式Java設計模式
- c#簡單工廠設計模式C#設計模式
- 設計模式之簡單工廠模式設計模式
- java設計模式-簡單工廠模式Java設計模式
- 設計模式(一)—— 簡單工廠模式設計模式
- 簡單工廠模式(設計模式02)設計模式
- 程式設計師工資高,卻有很多人想轉行,理由很簡單!程式設計師
- Java設計模式之簡單工廠、工廠方法和抽象工廠Java設計模式抽象
- Redux其實很簡單(原理篇)Redux
- Spring 測試:其實很簡單Spring
- 設計模式-C#實現簡單工廠模式設計模式C#
- 多語言版vfp程式設計簡單實現 (轉)程式設計
- 設計模式 --並不簡單的工廠模式設計模式
- C++ 簡單實現陣列類泛型程式設計示例C++陣列泛型程式設計
- 設計模式入門-簡單工廠模式設計模式
- golang設計模式之簡單工廠模式Golang設計模式
- 【Linux】設計模式-----簡單工廠模式Linux設計模式
- 大話設計模式:簡單工廠模式設計模式