14

我的問題很多。自從我看到了。 NET 4.5,我印象非常深刻。不幸的是,我所有的項目都是.NET 4.0,我不考慮遷移。所以我想簡化我的代碼。替換使用backgroundworker到async/tpl的方法(.NET 4.0)

目前,我的大部分代碼,通常需要足夠的時間定格畫面,我做到以下幾點:

BackgroundWorker bd = new BackgroundWorker(); 
bd.DoWork += (a, r) => 
    { 
     r.Result = ProcessMethod(r.Argument); 
    }; 
bd.RunWorkerCompleted += (a, r) => 
    { 
     UpdateView(r.Result); 
    }; 

bd.RunWorkerAsync(args); 

老實說,我已經厭倦了。當存在邏輯複雜的用戶交互時,這成爲一個大問題。

我想知道,如何簡化這個邏輯? (請記住,我使用.Net 4.0)我注意到了谷歌的一些事情,但沒有發現任何易於實現和適合我需求的東西。

我想下面這個方案:

var foo = args as Foo; 
var result = AsyncHelper.CustomInvoke<Foo>(ProcessMethod, foo); 
UpdateView(result); 

public static class AsyncHelper 
{ 
    public static T CustomInvoke<T>(Func<T, T> func, T param) where T : class 
    { 
     T result = null; 
     DispatcherFrame frame = new DispatcherFrame(); 
     Task.Factory.StartNew(() => 
     { 
      result = func(param); 
      frame.Continue = false; 
     }); 

     Dispatcher.PushFrame(frame); 

     return result; 
    } 
} 

我不知道的影響是在操縱調度框架。 但我知道它會工作的很好,例如,我可以在控制的所有事件中使用它,而不必費心凍結屏幕。 我對泛型類型,協方差,逆變的知識是有限的,也許這個代碼可以改進。

我想到其他的東西,使用Task.Factory.StartNewDispatcher.Invoke,但沒有看起來有趣和簡單的使用。任何人都可以給我點亮嗎?

回答

15

您應該只使用任務並行庫(TPL)。關鍵是爲當前SynchronizationContext指定TaskScheduler用於更新UI的任何延續。例如:

Task.Factory.StartNew(() => 
{ 
    return ProcessMethod(yourArgument); 
}) 
.ContinueWith(antecedent => 
{ 
    UpdateView(antecedent.Result); 
}, 
TaskScheduler.FromCurrentSynchronizationContext()); 
從一些異常處理訪問先行的 Result屬性時

除此之外,這是所有有過它。通過使用FromCurrentSynchronizationContext(),來自WPF的環境SynchronizationContext(即DispatcherSynchronizationContext)將用於執行延續。這與調用Dispatcher.[Begin]Invoke相同,但是完全從中抽象出來。

如果你想要變得更「清潔」,如果你控制ProcessMethod,我實際上會重寫那個返回一個Task並讓它自己如何得到啓動(仍然可以在內部使用StartNew)。通過這種方式,您可以從ProcessMethod可能想獨立執行的異步執行決策中抽象出調用者,而只需要擔心繼續等待結果的鏈接。

UPDATE 2013年5月22日

應當指出的是,與.NET 4.5的到來,並在C#中的異步語言支持這一規定的技術已經過時,你可以簡單地依靠這些功能來執行一個使用await Task.Run的特定任務,然後在分配器線程上自動重新執行。因此,像這樣:

MyResultType processingResult = await Task.Run(() => 
{ 
    return ProcessMethod(yourArgument); 
}); 

UpdateView(processingResult); 
+0

似乎是一個很好的選擇來簡化我的代碼,我已經可以想象如何使用它。儘管如此,我不能繼續使用相同的方法執行嗎? –

+0

@ J.Lennon我對當前的工作隊列實現還不夠了解,但是您無論如何都可以使這個相同的概念工作。如果您對實現有控制權,我會建議使用TPL DataFlow API(ActionBlock )作爲排隊機制。儘管如此,這是一個完整的獨立文章。 :) –

1

如何封裝,始終是在一個可重用的組件相同的代碼?您可以創建一個實現ICommand的Freezable,公開一個類型爲DoWorkEventHandler的屬性和一個Result屬性。在ICommand上。執行後,它將創建一個BackgroundWorker並將DoWork和Completed的代理連接起來,使用DoWorkEventHandler的值作爲事件處理程序,並以它將自己的Result屬性設置爲事件中返回的結果的方式處理Completed。

你會配置XAML的部件,使用轉換器將DoWorkEventHandler財產上的視圖模型的方法(我假設你有一個)進行綁定,並將綁定您查看到組件的結果屬性,所以它得到當Result執行更改通知時自動更新。

這個解決方案的優點是:它是可重複使用的,並且它僅適用於XAML,因此ViewModel中不再需要粘合代碼來處理BackgroundWorkers。如果您不需要後臺進程報告進度,甚至可能不知道它在後臺線程上運行,因此您可以在XAML中決定是要同步還是異步調用方法。

相關問題