BackgroundWorker:RunWorkerCompleted中的InvalidOperationException

时间:2014-12-07 15:12:37

标签: c# multithreading winforms excel-dna

我有一个带有backgroundWorker的WinForm:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SeoTools.Utils;

namespace SeoTools.UI
{
    public partial class UIProgress : Form
    {
        public UIProgress(DoWorkEventHandler doWorkEventHandler, RunWorkerCompletedEventHandler runWorkerCompletedEventHandler)
        {
            InitializeComponent();
            this.backgroundWorker.WorkerReportsProgress = true;
            this.backgroundWorker.WorkerSupportsCancellation = true;
            this.backgroundWorker.DoWork += doWorkEventHandler;
            this.backgroundWorker.RunWorkerCompleted += runWorkerCompletedEventHandler;
        }

        public void Start()
        {
            var foo = SynchronizationContext.Current;
            backgroundWorker.RunWorkerAsync();
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            btnStop.Enabled = false;
            btnStop.Text = "Stopping...";
            backgroundWorker.CancelAsync(); 
        }

       private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        try
        {
            wdgProgressBar.Value = e.ProgressPercentage;
            if (this.Visible == false)
            {
                this.ShowDialog();
                this.Update();
            }
        }
        catch (InvalidOperationException) {} 
    }

        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.Hide(); //Here I get a InvalidOperationException
            this.Dispose();
        }    
    }
}

我第一次运行它运行正常。但是第二次调用InvalidOperationException时我会this.Hide()

  

“附加信息:跨线程操作无效:控制'UIProgress'从其创建的线程以外的线程访问。”

奇怪的是第一次运行foo在Start()中是 WindowsFormsSyncronizationContext ,但在第二次尝试时它是 System.Threading.SyncronizationContext

我正在编写的应用程序是ExcelDna插件。

修改

Start()的调用如下:

 UIProgress uiProgress = new UIProgress(
                delegate(object sender, DoWorkEventArgs args)
                {
                   ....
                },
                delegate(object sender, RunWorkerCompletedEventArgs args)
                    {
                       ...
                    }
            );
            uiProgress.Start();

6 个答案:

答案 0 :(得分:7)

必须从UI线程上运行的代码调用您的Start()方法才能使BackgroundWorker正常运行。这不是你得到这个例外的时候。在您的方法中添加保护代码,以便您可以诊断这个事故:

    public void Start()
    {
        if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) {
            throw new InvalidOperationException("Bug! Code called from a worker thread");
        }
        backgroundWorker.RunWorkerAsync();
    }

现在,您可以在throw语句中设置断点,并使用调试器的“调用堆栈”窗口找出发生这种情况的原因。

答案 1 :(得分:3)

您正在后台线程上调用UI操作。这就是这种例外的原因。我会使用完全不同的方法来使进展形成最好的方法是使用Task with IProgress。使用它的另一种方式: `private void backgroundWorker_ProgressChanged(object sender,ProgressChangedEventArgs e)     {

  this.UpdateOnMainThread(
    ( ) =>
    {
      wdgProgressBar.Value = e.ProgressPercentage;
      if ( this.Visible == false )
      {
        this.ShowDialog( );
        this.Update( );
      }
    } );
}

private void UpdateOnMainThread( Action action )
{
  if ( this.InvokeRequired )
  {
    this.BeginInvoke( ( MethodInvoker ) action.Invoke);
  }
  else
  {
    action.Invoke( );
  }
}

private void backgroundWorker_RunWorkerCompleted( object sender , RunWorkerCompletedEventArgs e )
{
  this.UpdateOnMainThread(
    ( ) =>
    {
      this.Hide( ); //Here I get a InvalidOperationException
      this.Dispose( );
    } );

}`

答案 2 :(得分:1)

在表单上使用BeginInvoke()方法:

// http://msdn.microsoft.com/en-us/library/0b1bf3y3(v=vs.110).aspx

    private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        this.BeginInvoke(new InvokeDelegate(InvokeMethod));               
    }

    public delegate void InvokeDelegate();

    public void InvokeMethod()
    {
        this.Hide(); 
        this.Dispose();
    }

答案 3 :(得分:1)

我想你可以在这里找到一些帮助:BackgroundWorker hide form window upon completion。 但是,不要忘记分离BackgroundWorker事件并将其自行停止,就像在此处解释的那样:Proper way to Dispose of a BackGroundWorker。 问题可以在

this.Dispose();
backgroundWorker_RunWorkerCompleted事件中

。有了这个你正在处理表单页面。那是你想做的吗?或者您想要部署BackgroundWorker?处理表单页面所有资源都被释放,因此第二次执行this.Hide();可能是一个错误。

有关详细信息,您可以看到以下链接:C# Form.Close vs Form.DisposeForm.Dispose Method

答案 4 :(得分:1)

您必须检查此链接 How to update the GUI from another thread in C#?

可能有所有可能的答案

希望这个帮助

答案 5 :(得分:1)

您正在从无法操作UI的线程中运行对主线程的调用。 最简单的方法是使用匿名委托调用。

改变这个:

        if (this.Visible == false)
        {
            this.ShowDialog();
            this.Update();
        }

为此:

this.Invoke((MethodInvoker) delegate { 
        if (this.Visible == false)
        {
            this.ShowDialog();
            this.Update();
        }    
});

它不是最优化的方式,但在没有太多重新编码的情况下,工作非常快。 :)