了解InvalidAsynchronousStateException事件

什么时候抛出InvalidAsynchronousStateException?

我有以下代码:

control.InvokeRequired? control.Invoke(expression):expression();

在一些随机的情况下,我得到InvalidAsynchronousStateException并且我的应用程序挂起,在做了一些阅读后,似乎是在创建control的线程完成时将抛出此exception。 它是否正确? 如果是这样的话,情况似乎并非如此,除非某些事情导致我的应用程序崩溃,这种exception只是后果? 这可能吗?


System.ComponentModel.InvalidAsynchronousStateException:调用方法时发生错误。 目标线程不再存在。 System.Windows.Forms.Forvoke上的System.Windows.Forms.Control.WaitForWaitHandle(WaitHandle waitHandle)在System.Windows.Forms.Control.MarshaledInvoke(Control caller,Delegate方法,Object [] args,布尔同步)中的System.Windows.Forms.Control.WaitForWaitHandle(WaitHandle waitHandle)委托方法,Object [] args)位于c:\ Optimus \ Desktop \ Framework \中Optimus.Desktop.Framework.Spring.Aspects.UIThreadInterceptor.Invoke(IMethodInvocation调用)的System.Windows.Forms.Control.Invoke(Delegate方法) Spring \ Aspects \ UIThreadInterceptor.cs:Spring.Aop.Framework.AbstractMethodInvocation.Proceed()的第22行,Spring.Aop.Framework.DynamicProxy.AdvisedProxy.Invoke(对象代理,对象目标,类型targetType,MethodInfo targetMethod,MethodInfo proxyMethod,在Object.ComponentModel.Component.Finalize()的InheritanceAopProxy_4fda07e8828744839065a154b30915ee.Dispose(布尔处理)中的Object [] args,IList拦截器)


顺便说一句,我已经检查了这个答案并没有澄清我的疑问 – > 函数中的InvalidAsynchronousStateException检查控件是否需要调用

通常,当后台线程在UI线程已经退出后尝试调用UI线程时,会发生这种情况。 您是否有机会尝试在自己的线程中运行不同的表单,或者您是否从非UI线程中显示()表单,或者在表单显示之前调用()到表单?

背景如下:

1)每个控件(包括表格)都有一个句柄。 这用于将控件绑定回底层的Windows GDI对象。

2)创建控件本身时通常不会创建控件的句柄。 第一次控件是Show()n时创建句柄。

3)当调用控件时,.NET API会尝试使用它的句柄来定位控件的UI线程。 如果表单尚未显示,则CURRENT THREAD(调用线程)将被指定为UI线程

4)控件的UI线程应该运行一个消息循环来处理该控件(当你这样做时会自动发生,例如,Application.Run(someForm);

5)所以常见的错误是你从一个临时或线程池线程创建一个表单F,Invoke()或BeginInvoke(),它创建了表单的句柄,因此被指定为表单的UI线程。 然后后台线程退出,或者被线程池终止,或者只是无法运行消息循环,因为它不知道它已被指定为UI线程。 随后,对该表单的任何调用都会因此exception而失败。 抛出exception只是因为表单分配的“UI线程”没有运行消息循环。

有关如何发生这种情况的详细分析,请参阅Ivan的post: http : //www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html

我最近遇到过同样的问题。 我的表单包含几个不可见的用户控件,可能需要在应用程序的生命周期中稍后出现。 有时,这些请求来自后台线程。

问题是即使我在control.Invoke包含control.Visible = true ,控件实际上被分配给后台线程(如Chris的第3点所述),而不是表单的主UI线程。 对我来说一个简单的解决方法是在创建父窗体期间调用IWin32Window.Handle属性(例如从窗体的Load事件中调用)这样可以确保在主UI线程中创建控件而不使其可见。

 public partial class MyForm : Form { private void MyForm_Load(object sender, EventArgs e) { ForceControlCreation(control1); ForceControlCreation(control2); } private void ForceControlCreation(IWin32Window control) { // Ensures that the subject control is created in the same thread as the parent // form's without making it actually visible if not required. This will prevent // any possible InvalidAsynchronousStateException, if the control is later // invoked first from a background thread. var handle = control.Handle; } } 

正如其他人已经正确地表明,当一个UI组件被处理或完成(返回)而另一个线程仍然在同一UI组件上调用代码时,会发生这种情况。

这通常发生在用户关闭已由不同线程更新的窗口(表单)时,并且当更新频率较高时这种情况更为普遍(因为当用户关闭表单时调用不完整的可能性很高) 。

这可以通过以下方式优雅地处理:

  1. 设置一个标志以指示正在进行调用
  2. 拦截UI处理或返回
  3. 停止进一步(新)调用发生
  4. 等到现有调用完成
  5. 完成预定的处置或返回

下面的示例显示了如何优雅地处理最常见的场景(当表单在更新时关闭)。

该示例来自一个简单的表单,该表单具有通过公共方法(AddItem(string))从外部线程更新的列表框。

 private bool invokeInProgress = false; private bool stopInvoking = false 

调用代码

 public void AddItem(string newItem) { if (listView1.InvokeRequired) { if (stopInvoking != true) // don't start new invokes if the flag is set { invokeInProgress = true; // let the form know if an invoke has started listView1.Invoke(new Action(() => addItem(newItem))); // invoke invokeInProgress = false; // the invoke is complete } return; } listView1.Items.Add(newItem); listView1.Items[listView1.Items.Count - 1].EnsureVisible(); } 

拦截和管理表格结束事件

 private async void Form1_FormClosing(object sender, FormClosingEventArgs e) { if (invokeInProgress) { e.Cancel = true; // cancel the original event stopInvoking = true; // advise to stop taking new work // now wait until current invoke finishes await Task.Factory.StartNew(() => { while (invokeInProgress); }); // now close the form this.Close(); } } 

您可以通过公开允许用户(其他线程)知道表单正在关闭的方法或属性来进一步扩展这一点,以便调用者可以优雅地处理这种情况。

下面的示例显示了如果用户关闭显示表单,新属性(ShuttingDown)如何允许调用者正确处理其流。

表格上的新标志

 public bool ShuttingDown { get { return stopInvoking; } } 

呼叫者现在可以检测到问题

 static void Main() { Form1 frm = new Form1(); Task.Factory.StartNew(() => frm.ShowDialog(), TaskCreationOptions.LongRunning); int i = 0; while (i < 2000) { if (frm.ShuttingDown != true) // the clients can also be notified and allowed to handle the UI disruption { frm.addItem(Guid.NewGuid().ToString()); } else { MessageBox.Show("Form is closing. Stopping the process."); break; } i++; } MessageBox.Show("Program completed! i=" + i.ToString()); } 

您可以从这里阅读更多内容并下载示例项目: http : //www.ilearnttoday.com/c-sharp-the-destination-thread-no-longer-exists