为每个异步任务创建新的 BackgroundWorker
Creating new BackgroundWorker for every async task
每次我需要执行任何异步任务时,我都会创建一个新的 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();
但是当我通过将它与 action
组合或调用 t.ContinueWith()
执行 actionFinish()
时出现跨线程错误。 BGW 中不是这种情况,它不需要调用 RunWorkerCompleted
。我无法更改对此方法的每次调用以使其使用调用,我该怎么办?
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
任务(工作线程上的 运行ning)传递给 yourResultAction
任务( 运行ning 在 UI 线程上)。请注意,TaskScheduler.FromCurrentSynchronizationContext()
当然必须是 UI 线程上的 运行。
您可以将其包装在一个简单的辅助函数中,该函数将只执行两个操作,return 一个任务(原始任务或后续任务 - 这取决于您想要做什么)它)。
此外,Microsoft.Bcl.Async
将 .NET 4.5+ 的大部分 await
优点带到 .NET 4.0 - 毕竟,await
都在编译器中,它没有需要新的 运行 工作时间。使用 await
比使用延续要简单得多,尤其是在进行错误处理时。
BackgroundWorker 和任务都是 windows 进程,所以我不知道您为什么需要两者。 class 也是一个进程,但我喜欢使用单独的 class 而不是任务。
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
当两个或多个线程访问共享数据以进行写入时,就会出现竞争条件
同时。 (如何处理竞争条件将在本章后面的“同步
资源”部分。)如果您尝试从另一个线程更新 UI,.NET Framework 会抛出一个
InvalidOperationException 包含以下消息:“跨线程操作不
有效:控件 'ctrlName' 从线程以外的线程访问
创建于。
解决问题,改worker_RunWorkerCompleted方法
如下:
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (this.InvokeRequired) {
this.Invoke(
new Action(ActionFinish));
}
else {
ActionFinish();
}
}
在 worker_RunWorkerCompleted 方法中,您现在检查 InvokeRequired
属性。此 属性 在控件 class 中定义,因此存在于页面上的所有控件中。如果从 UI 线程调用它,InvokeRequired 设置为 false 和 true
否则。
Invoke 方法将委托作为第一个参数,这意味着任何方法都可以
放在那里。 new Action() 构造函数调用用于确保您
得到一个代表。如果您的方法具有不同的签名,则必须更改该构造函数
因此。 Invoke 方法的其余参数直接发送给方法
你想要运行。 Invoke 将方法调用放入队列中以供 UI 线程接收。
更好的解决方案?
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e { this.Dispatcher.Invoke(()=> //statement); }
所有情况调用Dispatcher.Invoke方法即可。这个电话
确保 UI 线程的 lambda 表达式 ()=> //statement 是 运行,
无论从哪个线程调用该方法。
每次我需要执行任何异步任务时,我都会创建一个新的 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();
但是当我通过将它与 action
组合或调用 t.ContinueWith()
执行 actionFinish()
时出现跨线程错误。 BGW 中不是这种情况,它不需要调用 RunWorkerCompleted
。我无法更改对此方法的每次调用以使其使用调用,我该怎么办?
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
任务(工作线程上的 运行ning)传递给 yourResultAction
任务( 运行ning 在 UI 线程上)。请注意,TaskScheduler.FromCurrentSynchronizationContext()
当然必须是 UI 线程上的 运行。
您可以将其包装在一个简单的辅助函数中,该函数将只执行两个操作,return 一个任务(原始任务或后续任务 - 这取决于您想要做什么)它)。
此外,Microsoft.Bcl.Async
将 .NET 4.5+ 的大部分 await
优点带到 .NET 4.0 - 毕竟,await
都在编译器中,它没有需要新的 运行 工作时间。使用 await
比使用延续要简单得多,尤其是在进行错误处理时。
BackgroundWorker 和任务都是 windows 进程,所以我不知道您为什么需要两者。 class 也是一个进程,但我喜欢使用单独的 class 而不是任务。
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
当两个或多个线程访问共享数据以进行写入时,就会出现竞争条件 同时。 (如何处理竞争条件将在本章后面的“同步 资源”部分。)如果您尝试从另一个线程更新 UI,.NET Framework 会抛出一个 InvalidOperationException 包含以下消息:“跨线程操作不 有效:控件 'ctrlName' 从线程以外的线程访问 创建于。
解决问题,改worker_RunWorkerCompleted方法 如下:
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (this.InvokeRequired) {
this.Invoke(
new Action(ActionFinish));
}
else {
ActionFinish();
}
}
在 worker_RunWorkerCompleted 方法中,您现在检查 InvokeRequired 属性。此 属性 在控件 class 中定义,因此存在于页面上的所有控件中。如果从 UI 线程调用它,InvokeRequired 设置为 false 和 true 否则。
Invoke 方法将委托作为第一个参数,这意味着任何方法都可以 放在那里。 new Action() 构造函数调用用于确保您 得到一个代表。如果您的方法具有不同的签名,则必须更改该构造函数 因此。 Invoke 方法的其余参数直接发送给方法 你想要运行。 Invoke 将方法调用放入队列中以供 UI 线程接收。
更好的解决方案?
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e { this.Dispatcher.Invoke(()=> //statement); }
所有情况调用Dispatcher.Invoke方法即可。这个电话 确保 UI 线程的 lambda 表达式 ()=> //statement 是 运行, 无论从哪个线程调用该方法。