當設計併發策略時,要將 "what做什麼"和 "how怎麼做"進行分離,
Prefer Futures to Baked-In "Async APIs"
一文介紹瞭如何使用語言的並行API通過異步
來實現這點。
普通同步性質的方法如下:
RetType DoSomething( InParameters ins, OutParameters outs ); |
如果DoSomething 將花費很長時間執行,無論它是否耗費CPU,比如從數據庫中加載一個數據就是很耗費時間的,那麼是不是在DoSomething執行的同時,我們做做其他事情呢?
比如:
result = DoSomething( this , that, outTheOther ); OtherWork(); |
如果OtherWork不依賴result 這個值,那麼我們讓DoSomething使用通常意義上的異步 執行就沒有問題。
但是如果OtherWork依賴上一步執行結果result,怎麼辦?
第一個方案是使用Begin/End 模式實現:
設計一個開始接口,如下
IAsyncResult BeginDoSomething( InParameters ins ); |
再設計一個結果接口,如下:
RetType EndDoSomething( IAsyncResult asyncResult,//上一接口計算的結果 OutParameters outs ); |
使用方式如下代碼:
IAsyncResult ar = BeginDoSomething( this , that ); result = DoSomething( this , that, outTheOther ); //在DoSomething運行同時,執行OtherWork OtherWork(); //將上面兩個融合Join, ar.AsyncWaitHandle.WaitOne(); //必要時需要等待 result = EndDoSomething( ar, outtheOther ); |
這個模式需要在最後等待,無論DoSomething和OtherWork哪個先做完,都要等待對方,這個性能會很差,當然該文從.NET框架等多個角度說明這個模式一些侷限,這裏不再引述,可以參考原文。
我們主要目的是要將“如何異步 運行這個工作 ”(也就是調用"how如何做")和做什麼分離開來。在上面這個案例中,我們關注的是讓DoSomething和OtherWork異步啓動,而DoSomething如何異步 運行(how),應該不是我們關心的,至少應該從上面這段調用代碼中分離出去。而 begin/end 模式並沒有幫助我們做到這點,相反是將How和What耦合在一起了。
讓我們來看看如何實現How和What分離解耦:
1.使用一個分離的Task任務調用來運行How的工作內容,根據你的語言平臺,比如如果是Java/.NET,使用pool.run( /*task*/ ),如果C++0x,使用async( /*task*/ ) .
2.使用futures來管理異步 運行的結果. 這實際就是類Java JDK中的Future<T> API, C++0x標準即將也會有,而.NET下一個版本使用的是Task<T>.
這樣,上面案例代碼如下:
//第一步 asynchronous call future<int > result = async( ()=<{ return CallSomeFunc(x,y,z); } ); //第二步 code here runs concurrently with CallSomeFunc //同時運行其他事情 //第三步 use result when it's ready (this might block) //在這裏使用異步 運行dosomething的結果 DoSomethingWith( result.value() ); |
這裏代碼爲什麼和前面代碼不完全一致,因爲我們根據How和What分離,更改了設計,如果前面案例的otherwork依賴DoSomething的結果,那麼,我們就要將otherwork內容進行分解,將otherwork中不依賴DoSomething結果的內容首先運行,也就是上面第二步,然後,使用在第三步,我們將兩個計算結果融合。
這種使用Future特性將How和What分離的模式,已經被使用在Jdonframework 6.2的Domain Events,實際就是Domain Events的內在機制,當領域模型中激活一個事件時,實際就是發送了一個消息,在Jdonframework 中,監聽消息實際執行如下代碼:
private void asynExecMessageListener(final DomainMessage message) { FutureTask futureTask = new FutureTask(new Callable<Boolean>() { public Boolean call() throws Exception { try { message.getMessageListener().action(message); } catch (Exception e) { .... return true ; } }); message.addFutureTask(futureTask);//運行futuretask executor.execute(futureTask); //相當於pool.run } |
應該說:JdonFramework 6.2是將EDA和Java並行計算,異步可伸縮性 融合在一起,在設計理念是先進的,本文也可以驗證這點。參考Domain Events異步應用