C# - 背景工人?

时间:2011-08-18 22:47:31

标签: c# multithreading exception backgroundworker

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

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);
        }
    }
}

这是我的逻辑,如果我以错误的方式解决这个问题,请纠正我:

  1. 我从GUI中抽象出类,因为它是〜2000行,需要它是自己的自包含对象。

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

  3. 我不知道怎么办是让后台工作人员知道我班上发生了错误。为了将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。 根据我的理解,未处理的异常会被转换为Error的{​​{1}}部分?当我尝试这个时,我得到以下内容:

    RunWorkerCompletedEventArgs

    Visual Studio说我的应用程序在// 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"); } } 上遇到了因未处理的异常而失败。

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

    c.Open()

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

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

4 个答案:

答案 0 :(得分:2)

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

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

作为你运行后台工作者的try的catch块,如果有意义的话。

答案 1 :(得分:2)

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

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并继续没有任何问题。

答案 2 :(得分:1)

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

在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

答案 3 :(得分:0)

每次DoWork中的操作完成,取消或抛出异常时,都会触发RunWorkerCompleted。然后在RunWorkerCompleted中,如果Error不为null,则检查RunWorkerCompletedEventArgs。在DoWork中发生异常时会自动设置Error属性。在这种特殊情况下不需要尝试捕获。

你走在正确的轨道上。