将使用backgroundworker的方法替换为async / tpl(.NET 4.0)

时间:2012-09-06 03:23:46

标签: wpf design-patterns pattern-matching task-parallel-library backgroundworker

我的问题很多。从我看到。 NET 4.5,我印象非常深刻。不幸的是我的所有项目都是.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的其他内容,但没有任何看似有趣且易于使用的内容。谁能给我一些启示?

3 个答案:

答案 0 :(得分:15)

您应该使用任务并行库(TPL)。关键是为更新UI的任何延续时间指定当前TaskScheduler的{​​{1}}。例如:

SynchronizationContext

除了访问前提条件Task.Factory.StartNew(() => { return ProcessMethod(yourArgument); }) .ContinueWith(antecedent => { UpdateView(antecedent.Result); }, TaskScheduler.FromCurrentSynchronizationContext()); 属性时的一些异常处理,这就是它的全部内容。通过使用Result,来自WPF的环境SynchronizationContext(即DispatcherSynchronizationContext)将用于执行延续。这与调用FromCurrentSynchronizationContext()相同,但您完全从中抽象出来。

如果你想要“更干净”,如果你控制ProcessMethod,我实际上会重写它以返回Dispatcher.[Begin]Invoke并让它拥有如何旋转(仍然可以在内部使用Task) 。这样你就可以从ProcessMethod可能想要自己做的异步执行决策中抽象调用者,而只需要担心链接继续等待结果。

更新2013年5月22日

应该注意的是,随着.NET 4.5的出现和C#中的异步语言支持,这种规定的技术已经过时,您可以简单地依靠这些功能来使用StartNew执行特定任务,然后执行这将自动在Dispatcher线程上再次发生。所以像这样:

await Task.Run

答案 1 :(得分:1)

如何封装在可重用组件中始终相同的代码?您可以创建一个实现ICommand的Freezable,公开Type DoWorkEventHandler的属性和Result属性。在ICommand.Executed上,它将创建一个BackgroundWorker并使用DoWorkEventHandler的值作为事件处理程序连接DoWork和Completed的委托,并以一种方式处理Completed,它将自己的Result属性设置为事件中返回的结果

您在XAML中配置组件,使用转换器将DoWorkEventHandler属性绑定到ViewModel上的方法(我假设您有一个),并将View绑定到组件的Result属性,以便更新当Result执行更改通知时自动。

此解决方案的优点是:它可重用,并且仅适用于XAML,因此ViewModel中不再需要用于处理BackgroundWorkers的粘合代码。如果您不需要后台进程来报告进度,它甚至可能不知道它在后台线程上运行,因此您可以在XAML中决定是要同步还是异步调用方法。

答案 2 :(得分:0)

几个月过去了,但这对你有帮助吗? Using async/await without .NET Framework 4.5