14

我的问题很多。自从我看到。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)我注意到 google 的一些东西,但没有发现任何易于实现且适合我需要的东西。

我认为这个解决方案如下:

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.StartNewand的其他东西Dispatcher.Invoke,但没有任何东西看起来有趣且易于使用。谁能给我一些光?

4

3 回答 3

15

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

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

除了在访问先行者的Result属性时进行一些异常处理之外,仅此而已。通过使用FromCurrentSynchronizationContext()来自 WPF 的环境 SynchronizationContext(即 DispatcherSynchronizationContext)将用于执行延续。这与 call 相同Dispatcher.[Begin]Invoke,但您完全从中抽象出来。

如果你想变得更“干净”,如果你控制 ProcessMethod 我实际上会重写它以返回 aTask并让它拥有它是如何旋转的(仍然可以在StartNew内部使用)。这样,您就可以将调用者从 ProcessMethod 可能想要自己做出的异步执行决策中抽象出来,而他们只需要担心链接延续以等待结果。

2013 年 5 月 22 日更新

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

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

UpdateView(processingResult);
于 2012-09-10T18:03:30.457 回答
1

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

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

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

于 2012-09-12T18:07:47.943 回答
0

几个月过去了,但这对您有帮助吗?
在没有 .NET Framework 4.5 的情况下使用 async/await

于 2013-03-22T10:45:31.433 回答