在没有WinForm控件的主线程上调用方法来调用Invoke或BeginInvoke

时间:2010-08-16 14:28:28

标签: c# multithreading backgroundworker

我想在后台线程上运行一个操作。当它完成后,我想检查发生的任何错误,并将它们重新抛出我的原始线程。

我正在使用背景工作者。在RunWorkerCompleted事件处理程序中抛出异常会导致未处理的异常 - 如果事件处理程序在后台线程上运行,这是有意义的。如果我有一个winform控件,我可以调用Invoke或BeginInvoke,但我没有winform控件在这个对象中,虽然它是一个winform项目。

如何重新抛出backgroundworker中发生的异常?

private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error != null)
        {              
            // I want to throw an exception here, without causing an unhandled exception and without being able to call Invoke or BeginInvoke on a WinForm control. 
        }
        else if (e.Cancelled)
        {
           // Do something useful
        }
        else
        {
            if (e.Result != null)
            {
               // Do something with the result
            }
        }
    }

我原以为RunWorkerCompleted事件处理程序将在原始调用线程上运行。也许背景工作者不是我在这种情况下所需要的。

6 个答案:

答案 0 :(得分:7)

无法将代码注入另一个正在运行的线程中。甚至操作系统都无法做到这一点。

Control.BeginInvoke的工作原理是将委托引用放入队列,然后使用PostMessage将用户消息发布到UI线程的消息队列中。 Application.Run消息循环查找此消息,当它发现它将队列弹出队列并执行它时。

关键是没有其他方法可以做你需要的东西,而你的主线程没有被编码以从另一个线程中寻找某种信号(或消息)。

<强>加

您声明这是一个WinForm应用程序,但您没有控件来使用BeginInvoke。

编辑:我提出了一个懒惰的负载而没有考虑它。控件最终可能会在错误的线程上创建

Application.Run之前预先创建一个控件,该控件在应用程序的生命周期中存在。您可以将此用于BeginInvoke。

编辑#3

那么我试着确保它有效,当然不是。你不能简单地创建一个通用的Control,它必须有一个HWND句柄。简单修复:像这样创建:

invokerControl = new Control();
invokerControl.CreateControl();

即使没有要调用的打开Form对象,也可以使用BeginInvoke。

答案 1 :(得分:1)

您可以从另一侧查看。我的意思是 - 在表单上放置计时器(将在与表单相同的主线程中运行),每秒一次 - 检查表单上的一些Exception字段(带有lock()),还有一些对象字段来检测操作是否已完成。然后从带有try ... catch的bgw_RunWorkerCompleted包装代码和catch(再次使用lock())将表单的Exception字段设置为捕获异常。但为什么不使用Invoke或BeginInvoke?

答案 2 :(得分:1)

如果您没有在UI线程上创建BGW实例,那么其RunWorkerCompleted事件将在任意线程池线程上运行。您在该线程上抛出的任何异常都是无法捕获的,并且会通过AppDomain.UnhandledException终止您的应用程序。

在这种情况下,对BGW的用处不多了。确保其事件在UI线程上运行是很好的。您也可以使用MethodInvoker.BeginInvoke()。您需要仔细考虑这一点,并确定当某些工作线程上的某些代码无法完成其工作时您将要做什么。通常不可能处理这样的事故,让程序崩溃和烧毁是正确的。

如果您确实需要某种方式来通知用户并尝试保持程序绊脚石,那么您真的应该在UI线程上创建BGW实例。并在RunWorkerCompleted事件处理程序中使用MessageBox.Show()。确保在执行此操作时恢复程序状态,您几乎肯定需要在DoWork()中使用catch子句来清理弹片。

答案 3 :(得分:0)

不要抛出异常。

在主应用程序线程订阅的后台工作程序中引发一个事件,然后在那里处理错误 - 必要时抛出异常。

答案 4 :(得分:0)

只需处理RunWorkerCompleted事件即可。此事件已为您同步

    BackgroundWorker bgw;

    private void button1_Click(object sender, EventArgs e)
    {
        bgw = new BackgroundWorker();
        bgw.DoWork +=new DoWorkEventHandler(bgw_DoWork);
        bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
        bgw.RunWorkerAsync(bgw);
    }

    void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error != null)
            textBox1.Text = e.Error.Message;
    }

    void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        throw new NotImplementedException();
    }

答案 5 :(得分:0)

如果您正在使用4.0,那么您可以切换到使用任务,而不是您想要的任务。见example