【问题标题】:Replacing methods that use backgroundworker to async / tpl (.NET 4.0)将使用 backgroundworker 的方法替换为 async / tpl (.NET 4.0)
【发布时间】:2012-08-30 19:51:36
【问题描述】:

我的问题很多。自从我看到。 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.StartNewDispatcher.Invoke 的其他东西,但没有什么看起来有趣且易于使用。谁能给我点灯?

【问题讨论】:

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


    【解决方案1】:

    您应该只使用任务并行库 (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 可能想要自己做出的异步执行决策中抽象出来,而他们只需要担心链接到一个延续来等待结果。

    2013 年 5 月 22 日更新

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

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

    【讨论】:

    • 似乎是简化代码的好选择,我已经可以想象如何使用它了。虽然,会有 q 不是我可以在同一个方法执行中继续执行的解决方案吗?
    • @J.Lennon 我对您当前的工作队列实现知之甚少,但您当然可以让这个相同的概念适用于它。如果您可以控制实现,我建议您使用 TPL DataFlow API (ActionBlock) 作为您的排队机制。这是一个完全独立的帖子来解决所有这些问题。 :)
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2013-10-11
        • 1970-01-01
        • 1970-01-01
        • 2021-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-28
        • 2012-10-08
        相关资源
        最近更新 更多