C# - 后台工作者?

Posted

技术标签:

【中文标题】C# - 后台工作者?【英文标题】:C# - Background Workers? 【发布时间】:2011-10-30 05:06:44 【问题描述】:

我有一个相当复杂的程序,所以我不会把整个东西都扔在这里。这是一个简化的版本:

class Report 
    private BackgroundWorker worker;

    public Report(BackgroundWorker bgWorker, /* other variables, etc */) 
        // other initializations, etc
        worker = bgWorker;
    

    private void SomeCalculations() 
        // In this function, I'm doing things which may cause fatal errors.
        // Example: I'm connecting to a database.  If the connection fails, 
        // I need to quit and have my background worker report the error
    



// In the GUI WinForm app:
// using statements, etc.
using Report;

namespace ReportingService 
    public partial class ReportingService : Form 

        // My background worker
        BackgroundWorker theWorker = new BackgroundWorker() 
            WorkerReportsProgress = true
        ;

        // The progress changed event
        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
            // e.UserState and e.ProgressPercentage on some labels, etc.
        

        // The do work event for the worker, runs the number crunching algorithms in SomeCalculations();
        void worker_DoWork(object sender, DoWorkEventArgs e) 
            Report aReport = e.Argument as Report;

            aReport.SomeCalculations();
        

        // The completed event, where all my trouble is.  I don't know how to retrieve the error,
        // or where it originates from.
        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
            // How, exactly, do I get this error message? Who provides it? How?
            if (e.Error != null) 
                MessageBox.Show("Error: " + (e.Error as Exception).ToString());
            
            else if (e.Cancelled) 
                MessageBox.Show("Canceled");
            
            // operation succeeded
            else 
                MessageBox.Show("Success");
            
        

        // Initialization of the forml, etc
        public ReportingService() 
            InitializeComponent();

            theWorker.ProgressChanged += worker_ProgressChanged;
            theWorker.DoWork += worker_DoWork;
            theWorker.RunWorkerCompleted += worker_RunWorkerCompleted;
        

        // A button that the user clicks to execute the number crunching algorithm
        private void sumButton_Click(object sender, EventArgs e) 
            Report myReport = new Report(theWorker, /* some other variables, etc */)
            theWorker.RunWorkerAsync(myReport);
        
    

这是我的逻辑,如果我以错误的方式进行,请纠正我:

    我将这个类从 GUI 中抽象出来,因为它有大约 2000 行,并且需要是它自己的自包含对象。

    我将后台工作人员传给我的班级,以便我可以报告我的数字运算进度。

我不知道该怎么做是让后台工作人员知道我的班级发生了错误。为了将 RunWorkerCompleted 参数作为异常获取,我的 try/catch 块需要去哪里,在 catch 块中我应该做什么?

感谢您的帮助!

编辑:

我尝试了以下方法来测试错误处理:

请记住,我损坏了我的数据库连接字符串以故意接收错误消息。

在我的课堂上我这样做:

// My number crunching algorithm contained within my class calls a function which does this:

// try 
    using (SqlConnection c = GetConnection())   // note: I've corrupted the connection string on purpose
        c.Open();  // I get the exception thrown here
        using (SqlCommand queryCommand = new SqlCommand(query, c))  /* Loop over query, etc. */ 
        c.Close();  
    
//  catch (Exception e)  

1。 据我了解,未处理的异常被强制转换为RunWorkerCompletedEventArgsError 部分?当我尝试这个时,我得到以下信息:

// In my winform application I initialize my background worker with these events:

void gapBW_DoWork(object sender, DoWorkEventArgs e) 
    Report aReport = e.Argument as Report;
    Report.Initialize();    // takes ~1 minute, throws SQL exception
    Report.GenerateData();  // takes around ~2 minutes, throws file IO exceptions


void gapBW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)    
    if (e.Error != null)   // I can't get this to trigger, How does this error get set?
        MessageBox.Show("Error: " + (e.Error as Exception).ToString());
    
    else if (e.Cancelled) 
        MessageBox.Show("Canceled: " + (e.Result).ToString());
    
    else 
        MessageBox.Show("Success");
    

Visual Studio 说我的应用程序在 c.Open() 上因未处理的异常而失败。

2。 当我在我的 DoWork 函数中放置一个 try/catch 块时:

void gapBW_DoWork(object sender, DoWorkEventArgs e) 
    try 
        Report aReport = e.Argument as Report;
        aReport.Initialize();   // throws SQL exceptions
        aReport.GenerateData(); // throws IO file exceptions
    
    catch (Exception except) 
        e.Cancel = true;    
        e.Result = except.Message.ToString();
    


void gapBW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)    
    if (e.Error != null)   // I can't get this to trigger, How does this error get set?
        MessageBox.Show("Error: " + (e.Error as Exception).ToString());
    
    else if (e.Cancelled) 
        MessageBox.Show("Canceled: " + (e.Result).ToString());
    
    else 
        MessageBox.Show("Success");
    

我得到一个 TargetInvocationException 在 Program.cs 中自动生成的 Application.Run(new ReportingService()); 行中未处理。我在 RunWorkerCompleted 上放置了一个断点,可以看到 e.Cancelled = true、e.Error = null 和 e.UserState = null。 e.Cancelled 中包含的消息只是“操作已取消”。我想我从 e.Result 的无效演员表中收到TargetInvocationException(因为它是空的)。不过,我想知道的是,为什么 e.Error 仍然为 null 并且 e.Canceled 不包含有关 为什么 操作被取消的任何有用信息?

3。 当我尝试在 DoWork 中设置 e.Canceled = true; 异常捕获时,我设法在我的 RunWorkerCompleted 函数中触发了 else if (e.Cancelled) 行。我认为这是为请求取消作业的用户保留的吗?我是否从根本上误解了后台工作人员的功能?

【问题讨论】:

【参考方案1】:

我尝试了这个小测试程序,它按预期工作:

static void Main(string[] args)

    var worker = new BackgroundWorker();

    worker.DoWork += (sender, e) =>  throw new ArgumentException(); ;
    worker.RunWorkerCompleted += (sender, e) => Console.WriteLine(e.Error.Message);
    worker.RunWorkerAsync();

    Console.ReadKey();

但是当我在调试器中运行这个程序时,我还在 throw 语句中收到了关于未处理异常的消息。但我只是再次按下F5,它继续没有任何问题。

【讨论】:

感谢大家的帮助,但这最终解决了我的问题。原来问题不是我对 C# 的误解,而是我对 Visual Studio 的误解。我已经养成了按 F5 运行程序的习惯,并认为当未处理异常时程序会崩溃。我没有意识到您可以再次按 F5 继续处理。 也不要忘记按 F10、F11 和 Shift-F11 的功能。 ;-)) 也许查看this question 也对您有所帮助。【参考方案2】:

你在正确的轨道上。在RunWorkerCompleted 事件中,e.Error 参数包含任何引发的异常。在这种情况下,你应该对待你的

if (e.Error != null) ...

作为您运行后台工作程序的try 的捕获块,如果这有意义的话。

【讨论】:

所以我不需要在 Report 类中围绕我的 SQL 查询进行 try/catch 吗?当我删除它时,我得到一个 SqlException is unhandled by user code 错误。我的工作人员的工作完成之前,异常永远不会发生? 如果您正在调试,只需按继续(默认为 F5),控制应流入您的 RunWorkerCompleted 事件处理程序,并应填充 e.Error。【参考方案3】:

每次 DoWork 中的操作完成、取消或抛出异常时都会触发 RunWorkerCompleted。然后在 RunWorkerCompleted 中检查 RunWorkerCompletedEventArgs 是否 Error 不为空。当 DoWork 中发生异常时,会自动设置 Error 属性。在这种特殊情况下不需要 try-catch。

你在正确的轨道上。

【讨论】:

【参考方案4】:

如果发生任何错误,请在 doEvent 的 catch 块中设置 e.Cancel = true。先设置WorkerSupportsCancellation属性为true。

在 DoWork 事件中。

private void bw_DoWork( object sender, DoWorkEventArgs e )
    
        try
         
            if( !backgroundWorkder.CancellationPending )
            
               //  ....
            
         
         catch
         
             if (bgWorker.WorkerSupportsCancellation && !bWorker.CancellationPending)
             
                 e.Cancel = true;
                 e.Result = "Give your error";
                 return;
             
         
   

在OnRunWorkerCompleted 方法中。

private void BW_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e )
    

     if( e.Cancelled )
        
            MessageBox.Show( e.Result.ToString());
            return;
        

如果在 DoEvent 中不做任何异常处理。 BackgroundWorker 本身会为您执行此操作。

如果在异步操作期间引发异常,则类 会将异常分配给 Error 属性。客户端 应用程序的事件处理程序委托应检查 Error 属性 在访问派生自的类中的任何属性之前 AsyncCompletedEventArgs;否则,该物业将提高 TargetInvocationException 及其 InnerException 属性持有 参考错误。

如果操作被取消,则 Error 属性的值为 null。

在那种情况下。

private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

    // First, handle the case where an exception was thrown.
    if (e.Error != null)
    
        MessageBox.Show(e.Error.Message);
    
    else if (e.Cancelled)
    
        // Next, handle the case where the user canceled the operation.         
    

更多详情请见here。

【讨论】:

以上是关于C# - 后台工作者?的主要内容,如果未能解决你的问题,请参考以下文章

c# - 如何强制关闭运行它自己的线程的后台工作者

C# ABP源码详解 之 BackgroundJob,后台工作

asp.net中javascript与后台c#交互

[Abp 源码分析]后台作业与后台工作者

C# 用自定义错误完成后台工作人员

后台工作者和 CPU 使用率