为每个异步任务创建新的BackgroundWorker

时间:2015-11-02 13:44:00

标签: c# .net performance backgroundworker

我每次需要执行任何异步任务时都会创建一个新的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()将其与actiont.ContinueWith()相结合时,我遇到了一个跨线程错误。在BGW中情况并非如此,它不需要调用RunWorkerCompleted。我无法更改对此方法的每次调用以使它们使用调用,我该怎么办?

3 个答案:

答案 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