代碼中頻繁出現下面描述的模式,
某些操作調用服務端接口獲取數據、執行復雜的數據解析,這些操作不能阻塞UI線程,必須異步執行,但通常這些操作執行完後需要對界面元素進行更新。
就是這麼個模式在代碼中頻繁出現。
之前代碼解決此問題的方式是使用thread,一般流程是下面這樣(工作任務一般在模型中有封裝,這些操作是阻塞的),
1. 首先我們必須把這些接口封裝成thread要求的接口,通常使用內部類(之前我們用的是VS05版本,沒有lambda)
2. 調用boost::thread
3. 執行完後PostMessage到某個窗體
4. 修改該窗體代碼,加上消息捕獲和消息處理方法
5. 等(取決想要什麼額外操作和程序員的寫法)
使用過類似方法的都知道有多痛苦,想寫的心都沒了,有時因此還會偷懶,比如在異步中更新界面元素。
通常上面說的每一步都是很短小且用一次的代碼,但使用上面的方式必須把小短的代碼分散到不同的源文件中,增加了對象間的引用交互,最主要的是麻煩,每次需要執行該模式時,執行上面的每一步都是痛苦的。
想要的其實很簡單:異步執行指定的動作,等執行完後記得更新界面元素。
既然是頻繁出現的模式,那就把該模式封裝起來,接口中只需填寫
1. 需要執行什麼動作
2. 如何更新界面元素
參照C#中的then,我們最終要的大概是如下寫法,
aync([=]()->DWORD_PTR {
std::string result;
m_impl->user->getCrashResult(begin->first, result);
return CrashResult::GetResultCount(result);
}).then([=](DWORD_PTR result) {
UpdateCrashResult(unsigned int)begin->first.fileId(), (int)result);
});
aync中的代碼在工作線程中執行,then中的代碼在UI線程中執行(現在VS版本是VS10,有lambda支持)。
實現想法如下,
aync內部會把lambda以及then中的行為封裝為對象,存儲到一個數組中,aync要求的接口是返回DWORD_PTR類型的無參函數,返回的值在then中作為參數,DWORD_PTR就是一個int型,說是指針也好,這東西在Windows平台傳參很常見,個人很喜歡這個,因為用法很靈活。
aync中會把lambda轉換成無參無返回值類型,以適應thread接口,之後放入線程池中,
BimWorks::GetAsyncAction().Append([&]() {
this->result_ = func_();
GetBWAsyncInvoke().Submit(*this);
});
在func_執行完後調用 GetBWAsyncInvoke().Submit(*this); 該方法執行post消息到主線程動作,這裡使用一個隱藏的static窗體作為消息的接收方。
消息處理函數簡單的從隊列中找到該lambda對應的對象,執行保存的then動作。執行完後從隊列中移除該對象。
完成。
這個實現只是一個初步的方法,有些細節需要完善的。
這種方式只適用一種固定的模式,即最開始描述的模式,thread太底層了,我們往往需要更高層的模式,比如task這樣的概念,VS10中的PPL庫提供了其它一些模式,比如task,並行算法、容器,流水線,協作等,感覺都不是我們想要的這種非常輕量的寫法。