我每次需要执行任何异步任务时都会创建一个新的BackgroundWorker
,并且在完成工作后我Dispose
但是我怀疑是否完全处理它因为Dispose方法是由System。ComponentModel.Component
在程序的典型运行中,可能会创建数千个程序。如果有数百万(为了安全起见)可以创建或者我应该以其他方式做到这一点吗?
大多数异步工作都是I / O,我不能使用async / await,因为我使用的是.NET 4(不能使用4.5)。
我用这个方法包装了整个事情:
public void AsyncDo(Action Action, Action ActionFinish = null)
{
using (BackgroundWorker bw = new BackgroundWorker()) {
bw.DoWork += () => Action();
bw.RunWorkerCompleted += (s, e) =>
{
if (ActionFinish != null)
ActionFinish();
if (e.Error != null)
OnException(e.Error);
};
bw.RunWorkerAsync();
}
}
根据答案,我用
更新了它Task t = new Task(action);
t.Start();
但是当我actionFinish()
将其与action
或t.ContinueWith()
相结合时,我遇到了一个跨线程错误。在BGW中情况并非如此,它不需要调用RunWorkerCompleted
。我无法更改对此方法的每次调用以使它们使用调用,我该怎么办?
答案 0 :(得分:4)
BackgroundWorker
专门设计用于允许代码在UI线程的后台运行,同时还允许与UI进行简单同步。
如果您需要执行任何异步任务,请使用Task
- 这就是它的设计目标。如果您没有.NET 4+,则需要使用Thread
(或更好,ThreadPool.QueueUserWorkItem
) - 但这更复杂(看起来看似简单)。与往常一样,http://www.albahari.com/threading/应该是您尝试实现多线程(或一般的异步代码)的起点:)
修改强>
要获得与BackgroundWorker
最初的行为接近的行为,您只需添加一个继续以在UI线程上运行:
var task = Task.Factory.StartNew(yourAction);
task.ContinueWith(yourResultAction, TaskScheduler.FromCurrentSynchronizationContext());
所有这些都是强类型的,因此很容易将yourAction
任务(在工作线程上运行)中的任意值传递给yourResultAction
任务(在UI线程上运行)。请注意,TaskScheduler.FromCurrentSynchronizationContext()
必须在UI线程上运行。
你可以将它包装在一个简单的辅助函数中,它只接受这两个动作,并返回一个任务(原始任务或继续任务 - 取决于你想用它做什么)。
此外,Microsoft.Bcl.Async
带来了.NET 4.5 +的大部分await
优点到.NET 4.0 - 毕竟,await
都在编译器中,它不需要新的运行时工作。使用await
比使用continuation更简单,尤其是在进行错误处理时。
答案 1 :(得分:3)
当两个或多个线程访问共享数据(用于写入)时,会发生竞争条件 同时。 (如何处理竞争条件将在本章后面的“同步”中介绍 资源“部分。)如果您尝试从另一个线程更新UI,.NET Framework会抛出一个 InvalidOperationException包含以下消息:“不进行跨线程操作 valid:控制'ctrlName'从线程以外的线程访问 创建于。“
要解决此问题,请更改worker_RunWorkerCompleted方法 如下:
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (this.InvokeRequired) {
this.Invoke(
new Action(ActionFinish));
}
else {
ActionFinish();
}
}
在worker_RunWorkerCompleted方法中,现在检查InvokeRequired 属性。此属性在Control类中定义,因此存在于页面上的所有控件上。如果从UI线程调用它并且为true,则InvokeRequired设置为false 否则。
Invoke方法将委托作为第一个参数,这意味着任何方法都可以 放在那里。新的Action()构造函数调用用于确保您 得到一个代表。如果您的方法具有不同的签名,则必须更改该构造函数 因此。 Invoke方法的其余参数直接发送到方法 你想跑。调用将方法调用放在队列中以供UI线程选取。
更好的解决方案?
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e { this.Dispatcher.Invoke(()=> //statement); }
在所有情况下调用Dispatcher.Invoke方法就足够了。这个电话 确保lambda表达式()=> //语句由UI线程运行, 无论该方法被调用哪个线程。
答案 2 :(得分:-4)
BackgroundWorker和任务都是Windows进程,所以我不知道为什么你需要这两个。类也是一个过程,但我喜欢使用单独的类而不是任务。
Imports System.ComponentModel
Module Module1
Sub Main()
Dim backgroundWorker As New MyBackGroundWorker
backgroundWorker.Dispose()
End Sub
End Module
Class MyBackGroundWorker : Implements IDisposable
Dim backgroundWorker As New BackgroundWorker
Sub Dispose() Implements IDisposable.Dispose
Me.Dispose()
End Sub
End Class