MVP 中使用 WinForms 進行非同步呼叫的最佳實踐
我在 WinForms 項目中使用 Model-View-Presenter 模式,我遇到的一個問題(在許多問題中)是當表單告訴展示者做某事,然後在展示者去做時沒有反應。幸運的是,在我的項目中,讓所有展示者呼叫非同步都沒有問題,問題是如何做到這一點?
是否應該將每個展示者呼叫都包含在一個新的執行緒創建中?*
new Thread(()=>_presenter.DoSomething()).Start();這裡的最佳實踐是什麼?如果使用者按下“中止你正在做的事情”按鈕怎麼辦?如何優雅地中止?
.* 實際上,我可能只是在展示者上使用某種代理來執行此操作,而不是將執行緒創建放在 WinForm 中
我只能聲稱我已經考慮過這一點(在閱讀您的問題之前;)。首先,我會安裝這實際上很重要的地方;例如數據庫訪問阻塞點。如果在“UI”上下文中存在不應該執行的地方(您可以從http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.current.aspx在 UI 執行緒中保存它然後稍後與非 UI 同步上下文進行比較)然後 Debug.BitchAndMoan() 關於它。任何更長的計算(“應該”在它們自己的流形中都清楚地分開,對;)應該斷言。
我想你至少應該通過屬性配置展示者函式的執行類型,然後由代理遵守。(以防萬一您想以串列方式完成某些事情)。
取消任務實際上是展示者的問題,但您必須有一個參考對象,告訴您要停止什麼。如果您採用代理方式,那麼您可以使用 IAsyncResult 將創建的執行緒拾取到任務列表中,但是如果允許並行呼叫多次相同的操作,那麼決定取消哪個執行緒仍然是一個問題。因此,您必須在啟動任務時為任務提供合適的特定於呼叫的名稱;這意味著 View 方面的邏輯太多 - > Presenter 可能應該要求 View 詢問使用者應該處理哪一項任務。
我的經驗是,這通常只是通過使用事件(SCSF 風格)來解決。如果從頭開始做,我會選擇代理方式,因為 SCSF 在很多方面都很痛苦,以至於我懷疑它的設計師的理智。
我通常將任何可能(實際上)花費一兩秒以上的操作放入單獨的任務中,例如:
public interface ITask { void ExecuteTask (ITaskExecutionContext context); void AfterSuccess(ITaskExecutionContext context); void AfterFailure(ITaskExecutionContext context); void AfterAbortion(ITaskExecutionContext context); }我還有一個用於執行此類任務的抽象:
public interface ITaskExecutor : IDisposable { void BeginTask(ITask task); void TellTaskToStop(); }其中一種實現
ITaskExecutor是使用BackgroundWorker:public class BackgroundTaskExecutor : ITaskExecutor { public void BeginTask(ITask task) { this.task = task; worker = new BackgroundWorker (); worker.DoWork += WorkerDoWork; worker.RunWorkerCompleted += WorkerRunWorkerCompleted; worker.WorkerSupportsCancellation = true; worker.RunWorkerAsync(); } ... }我嚴重依賴依賴注入和 IoC 將事物連接在一起。在展示者中,我只是呼叫如下內容:
GoAndDontReturnUntilYouBringMeALotOfMoneyTask task = new GoAndDontReturnUntilYouBringMeALotOfMoneyTask(parameters); taskExecutor.BeginTask(task);然後連接取消/中止按鈕,以便它們告訴任務執行器/任務中止。
它實際上比這裡介紹的要復雜一些,但這是一般的想法。