Wpf 将使用backgroundworker的方法替换为异步/tpl(.NET 4.0)
我的问题很多。自从我看到。NET4.5,给我留下了深刻的印象。不幸的是,我所有的项目都是.NET4.0,我没有考虑迁移。所以我想简化我的代码 目前,我的大多数代码通常需要足够的时间来冻结屏幕,我执行以下操作:Wpf 将使用backgroundworker的方法替换为异步/tpl(.NET 4.0),wpf,design-patterns,pattern-matching,task-parallel-library,backgroundworker,Wpf,Design Patterns,Pattern Matching,Task Parallel Library,Backgroundworker,我的问题很多。自从我看到。NET4.5,给我留下了深刻的印象。不幸的是,我所有的项目都是.NET4.0,我没有考虑迁移。所以我想简化我的代码 目前,我的大多数代码通常需要足够的时间来冻结屏幕,我执行以下操作: BackgroundWorker bd = new BackgroundWorker(); bd.DoWork += (a, r) => { r.Result = ProcessMethod(r.Argument); }; bd.RunWorkerCo
BackgroundWorker bd = new BackgroundWorker();
bd.DoWork += (a, r) =>
{
r.Result = ProcessMethod(r.Argument);
};
bd.RunWorkerCompleted += (a, r) =>
{
UpdateView(r.Result);
};
bd.RunWorkerAsync(args);
老实说,我已经厌倦了。当存在逻辑复杂的用户交互时,这就成了一个大问题
我想知道,如何简化这个逻辑?(请记住,我使用的是.NET4.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;
}
}
var foo=args作为foo;
var result=AsyncHelper.CustomInvoke(ProcessMethod,foo);
更新视图(结果);
公共静态类AsyncHelper
{
公共静态T CustomInvoke(Func Func,T param),其中T:class
{
T结果=null;
DispatcherFrame=新DispatcherFrame();
Task.Factory.StartNew(()=>
{
结果=函数(参数);
frame.Continue=false;
});
调度员.推架(框架);
返回结果;
}
}
我不确定对操纵dispatcher帧的影响。
但我知道它会工作得很好,例如,我可以在控件的所有事件中使用它,而不用费心冻结屏幕。
我对泛型类型、协方差、逆变换的了解有限,也许这段代码可以改进
我想到了使用
Task.Factory.StartNew
和Dispatcher.Invoke
的其他方法,但没有什么有趣和简单的方法。谁能给我点启发吗?你应该使用任务并行库(TPL)。关键是为当前的SynchronizationContext
指定TaskScheduler
,用于更新UI的任何延续。例如:
Task.Factory.StartNew(() =>
{
return ProcessMethod(yourArgument);
})
.ContinueWith(antecedent =>
{
UpdateView(antecedent.Result);
},
TaskScheduler.FromCurrentSynchronizationContext());
除了访问antecedent的Result
属性时的一些异常处理之外,它还有其他功能。通过使用FromCurrentSynchronizationContext()
来自WPF的环境同步上下文(即DispatchersSynchronizationContext)将用于执行延续。这与调用调度程序相同。[Begin]Invoke
,但您完全脱离了它
如果你想变得更“干净”,如果你控制ProcessMethod,我实际上会重写它以返回一个任务
,并让它知道如何旋转(仍然可以在内部使用StartNew
)。这样,您就可以将调用方从ProcessMethod可能希望自己做出的异步执行决策中抽象出来,取而代之的是,调用方只需担心链接到一个continuation以等待结果
更新日期:2013年5月22日
应该注意的是,随着.NET 4.5的出现和C#中的异步语言支持,这种规定的技术已经过时,您可以简单地依靠这些功能使用wait task执行特定任务。运行,然后在调度程序线程上自动执行。比如说:
MyResultType processingResult = await Task.Run(() =>
{
return ProcessMethod(yourArgument);
});
UpdateView(processingResult);
在可重用组件中封装始终相同的代码如何?您可以创建一个Freezable,它实现ICommand,公开DoWorkEventHandler类型的属性和结果属性。在ICommand.Executed上,它将创建一个BackgroundWorker,并将DoWork和Completed的委托关联起来,使用DoWorkEventHandler的值作为事件处理程序,并以将自己的Result属性设置为事件中返回的结果的方式处理Completed
您可以在XAML中配置组件,使用转换器将DoWorkEventHandler属性绑定到ViewModel上的方法(我假设您已经有了一个),并将视图绑定到组件的Result属性,这样当Result发出更改通知时,它会自动更新
此解决方案的优点是:它是可重用的,并且仅适用于XAML,因此ViewModel中不再有仅用于处理BackgroundWorkers的粘合代码。如果您不需要后台进程来报告进度,它甚至可能不知道它在后台线程上运行,因此您可以在XAML中决定是同步还是异步调用方法 几个月过去了,但这能帮到你吗?
似乎是简化代码的好选择,我已经可以想象如何使用它了。虽然,没有一个解决方案可以在相同的方法执行中继续?@J.Lennon我对您当前的工作队列实现了解不够,但是您肯定可以让相同的概念在任何情况下都可以使用。如果您可以控制实现,我建议使用TPL数据流API(ActionBlock)作为您的排队机制。不过,这是一篇完整的独立文章,可以解决所有这些问题。:)