C# 背景工人?

C# 背景工人?,c#,multithreading,exception,backgroundworker,C#,Multithreading,Exception,Backgroundworker,我有一个相当复杂的程序,所以我不会把整个东西都放在这里。以下是一个简化版本: class Report { private BackgroundWorker worker; public Report(BackgroundWorker bgWorker, /* other variables, etc */) { // other initializations, etc worker = bgWorker; } private

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

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) { }
    
    一,。 据我所知,一个未处理的异常会被强制转换到
    RunWorkerCompletedEventArgs
    错误部分?当我尝试此方法时,我得到以下结果:

    // 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()
    失败而阻塞,出现未处理的异常

    二,。 当我在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");
        }
    }
    
    我在自动生成的
    应用程序.Run(new ReportingService())的Program.cs中得到一个
    targetingException
    未处理的异常行。我在RunWorkerCompleted上放置了一个断点,可以看到e.Cancelled=true、e.Error=null和e.UserState=null。e.Cancelled中包含的消息只是“操作已取消”。我想象我从一个无效的e.Result强制转换(因为它是空的)接收到
    targetingException
    。但是我想知道的是,为什么e.Error仍然为null,而e.cancelled没有包含任何关于为什么取消操作的有用信息

    三,。
    当我尝试设置
    e.cancelled=true时在异常捕获的DoWork中,我设法触发了
    else if(例如取消){
    我的
    RunWorkerCompleted
    函数中的行。我认为这是为请求取消作业的用户保留的?我根本误解了后台工作人员的工作方式吗?

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

    在嫁妆活动中

    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;
                 }
             }
       }
    
    在方法上

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

    如果在异步操作期间引发异常,则类 将异常分配给错误属性。客户端 应用程序的事件处理程序委托应检查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.         
        }
    }
    

    有关更多详细信息,请参阅。

    您的思路正确。在
    RunWorkerCompleted
    事件中,e.Error参数包含引发的任何异常。在这种情况下,您应该处理

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

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

    每次在DoWork中的操作完成、取消或引发异常时,都会触发RunWorkerCompleted。然后在RunWorkerCompleted中,如果错误不为null,则检查RunWorkerCompletedEventArgs。错误属性自动设置为DoWork中出现en异常。在这种特殊情况下不需要try-catch


    您的思路是正确的。

    我尝试了这个小测试程序,它的工作原理与预期相符:

    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,它继续运行,没有任何问题。

    因此我不需要在我的报表类中尝试/捕获SQL查询?当我删除它时,我得到一个SqlException未被用户代码err处理或者。异常永远不会传播到my worker的工作完成?如果正在调试,只需按“继续”(默认情况下为F5)控件应该流入RunWorker完成的事件处理程序和e中。错误应该被填充。感谢大家的帮助,但这最终解决了我的问题。事实证明,问题不是我对C#的误解,而是我对Visual Studio的误解。我养成了按F5运行程序的习惯,并认为n异常未经处理,程序将崩溃。我不知道您可以再次按F5继续处理。另外,请不要忘记按F10、F11和Shift-F11的功能。;-)也许查看一下也能帮到您。
    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();
    }