c# - 将 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 而不是用任务/异步结构污染我的代码?什么是最佳实践?
解决方案
您正在重新发明轮子,因为新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()
推荐阅读
- java - 为不同的配置文件定义具有相同方法名称的 Spring Bean
- python - 用于在文件夹层次结构中标记(科学)二进制文件的 XML
- java - 出 onResponse 结果 NULL
- go - 使用 Go 读写 Yaml 文件
- ios - SwiftUI @State 变量没有被取消初始化
- excel - 如果函数在 Excel 中返回文本而不是数字?
- android - 如何在 Android/kotlin 中使用 CLIPS 规则引擎获取输出
- excel - 增强宏
- java - 基于自定义 Nifi 处理器的 Maven 构建在测试中失败,java.lang.UnsupportedOperationException
- r - 当子集的长度变为零时,如何简洁地处理子集?