首页 > 解决方案 > 将 BackgroundWorker 转换为 Task,最佳实践?

问题描述

我有一个类 SimpleTask,它看起来像这样:

    public class SimpleTask<T>
    {
        private readonly Action<Exception> _errorAction;
        private readonly Func<T> _produce;
        private readonly Action<T> _then;

        public SimpleTask(Func<T> produce, Action<T> then, Action<Exception> errorAction)
        {
            _then = then ?? throw new ArgumentNullException(nameof(then));
            _errorAction = errorAction ?? throw new ArgumentNullException(nameof(errorAction));
            _produce = produce ?? throw new ArgumentNullException(nameof(produce));
        }


        public void Run()
        {
            using (var backgroundWorker = new BackgroundWorker())
            {
                var item = default(T);
                backgroundWorker.DoWork += (_, e) => item = _produce();
                backgroundWorker.RunWorkerCompleted += (_, e) =>
                {
                    if (e.Error != null)
                    {
                        _errorAction(e.Error);
                        return;
                    }

                    _then(item);
                };
                backgroundWorker.RunWorkerAsync();
            }
        }
    }

我想使用 Task 而不是 BackgroundWorker 但我最终得到了这样的结果:

    public class SimpleTask<T>
    {
        private readonly Action<Exception> _errorAction;
        private readonly Func<T> _produce;
        private readonly Action<T> _then;

        public SimpleTask(Func<T> produce, Action<T> then, Action<Exception> errorAction)
        {
            _then = then ?? throw new ArgumentNullException(nameof(then));
            _errorAction = errorAction ?? throw new ArgumentNullException(nameof(errorAction));
            _produce = produce ?? throw new ArgumentNullException(nameof(produce));
        }

        public void Run()
        {
            try
            {
                var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext();
                Task.Run(_produce,CancellationToken.None).ContinueWith(t =>
                {
                    if (t.IsFaulted)
                    {
                        _errorAction(t.Exception);
                    }
                    else if (t.IsCompleted)
                    {
                        _then(t.Result);
                    }
                }, CancellationToken.None,TaskContinuationOptions.ExecuteSynchronously,synchronizationContext);

                
            }
            catch (Exception ex)
            {
                _errorAction(ex);
            }
        }

    }

毕竟这不一样。在我的单元测试中,我必须添加:

        [SetUp]
        public void TestSetUp()
        {
           SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
        }

我想知道我是否应该继续使用 BackgroundWorker 而不是用任务/异步结构污染我的代码?什么是最佳实践?

标签: c#async-await

解决方案


您正在重新发明轮子,因为新async功能会为您处理大部分内容。

我假设你想:

  • 在后台任务中运行返回结果的计算绑定方法
  • 处理该方法抛出的任何异常
  • 如果没有发生异常,则访问返回值

这是一个如何使用await.

在此示例中,计算绑定方法是int computeBoundFunction(). 该代码假定您有一个 Windows 窗体窗体,其中包含一个名为的按钮button1和一个名为的多行文本框textBox1

async void button1_Click(object sender, EventArgs e)
{
    textBox1.AppendText("Starting task\r\n");

    try
    {
        int result = await Task.Run(computeBoundFunction);

        // Instead of your "then" action, just call the code here.
        // In this example, I'm just appending to a multiline text box.
        // This runs on the UI thread.

        textBox1.AppendText("Task returned " + result);
    }

    // Instead of your "errorAction" action, handle exceptions here.
    // Note that this runs on the UI thread, so you can update controls safely at this point.

    catch (Exception exception)
    {
        textBox1.AppendText("Exception: " + exception.Message);
    }
}

int computeBoundFunction()
{
    Thread.Sleep(2000); // Emulate workload.
    return 42;

    // Comment out the return above and uncomment the line below to test the exception handling:
    //throw new InvalidOperationException("Test exception");
}

请注意,通常您永远不会使用async void而不是,但是对于本示例中async Task的事件处理程序,此规则是宽松的。button1_Click()


推荐阅读