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

J. *_*non 14 wpf design-patterns pattern-matching backgroundworker task-parallel-library

我的问题很多.从我看到.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);
Run Code Online (Sandbox Code Playgroud)

老实说,我已经厌倦了.当存在逻辑复杂的用户交互时,这成为一个大问题.

我想知道,如何简化这种逻辑?(请记住,我在.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我不确定影响是否会影响调度程序框架.但我知道它会很好用,例如,我可以在所有控件事件中使用它,而不必费心冻结屏幕.我对泛型类型,协方差,逆变的知识是有限的,也许这个代码可以改进.

我想到了用其他的东西Task.Factory.StartNewDispatcher.Invoke,但没有什么似乎有趣,简单易用.任何人都可以给我一些亮点吗?

Dre*_*rsh 15

您应该只使用任务并行库(TPL).关键是TaskSchedulerSynchronizationContext更新UI的任何延续指定当前的当前值.例如:

Task.Factory.StartNew(() =>
{
    return ProcessMethod(yourArgument);
})
.ContinueWith(antecedent =>
{
    UpdateView(antecedent.Result);
},
TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)

除了访问前提的Result属性时的一些异常处理,这就是它的全部内容.通过使用FromCurrentSynchronizationContext()来自WPF的环境SynchronizationContext(即DispatcherSynchronizationContext)将用于执行延续.这与调用相同Dispatcher.[Begin]Invoke,但您完全从中抽象出来.

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

更新2013年5月22日

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

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

UpdateView(processingResult);
Run Code Online (Sandbox Code Playgroud)