C# 了解InvalidAsynchronousStateException发生情况
何时抛出InvalidAsynchronousStateException 我有以下代码: control.InvokeRequired? control.Invoke(表达式): 表达式() 在一些随机情况下,我会得到InvalidAsynchronousStateException,并且我的应用程序挂起,在进行一些读取之后,当创建C# 了解InvalidAsynchronousStateException发生情况,c#,winforms,multithreading,threadpool,C#,Winforms,Multithreading,Threadpool,何时抛出InvalidAsynchronousStateException 我有以下代码: control.InvokeRequired? control.Invoke(表达式): 表达式() 在一些随机情况下,我会得到InvalidAsynchronousStateException,并且我的应用程序挂起,在进行一些读取之后,当创建控件的线程完成时,似乎会抛出此异常。这是正确的吗?如果是这样的话,情况似乎并非如此,除非有什么东西使我的应用程序崩溃,而这个异常只是一个结果?这可能吗 Syste
控件的线程完成时,似乎会抛出此异常。这是正确的吗?如果是这样的话,情况似乎并非如此,除非有什么东西使我的应用程序崩溃,而这个异常只是一个结果?这可能吗
System.ComponentModel.InvalidaSynchronousState异常:调用该方法时出错。目标线程不再存在。
位于System.Windows.Forms.Control.WaitForWaitHandle(WaitHandle-WaitHandle)
位于System.Windows.Forms.Control.MarshaledInvoke(控件调用方、委托方法、对象[]参数、布尔同步)
位于System.Windows.Forms.Control.Invoke(委托方法,对象[]args)
在System.Windows.Forms.Control.Invoke(委托方法)中
在c:\Optimus\Desktop\Framework\Spring\Aspects\UIThreadInterceptor.cs中的Optimus.Desktop.Framework.Spring.Aspects.UIThreadInterceptor.Invoke(IMethodInvoke)中
在Spring.Aop.Framework.AbstractMethodInvocation.Procedure()中
在Spring.Aop.Framework.DynamicProxy.AdvisedProxy.Invoke(对象代理,对象目标,类型targetType,MethodInfo targetMethod,MethodInfo proxyMethod,对象[]args,IList拦截器)
在继承时Approxy_4fda07e8828744839065a154b30915ee.Dispose(布尔处理)
在System.ComponentModel.Component.Finalize()中
顺便说一句,我已经检查了这个答案,没有澄清我的疑问->通常情况下,当后台线程试图在UI线程已经退出后调用UI线程时,会发生这种情况。您是否有可能尝试在各自的线程中运行不同的表单,或者您是否从非UI线程显示()表单,或者在表单显示之前调用()表单
背景如下:
1) 每个控件(包括窗体)都有一个句柄。这用于将控件绑定回基础windows GDI对象
2) 创建控件本身时,通常不会创建控件的句柄。句柄是在控件第一次为Show()n时创建的
3) 调用控件时,.NETAPI尝试使用控件的句柄定位控件的UI线程如果表单尚未显示,则当前线程(调用线程)将被指定为UI线程
4) 控件的UI线程需要运行一个消息循环来处理该控件(当您执行此操作时,会自动发生,例如Application.run(someForm))
5) 因此,常见的错误是从临时线程或线程池线程创建表单F、Invoke()或BeginInvoke(),从而创建表单句柄,并因此被指定为表单的UI线程。然后后台线程退出,或者被线程池终止,或者根本无法运行消息循环,因为它不知道它已被指定为UI线程。随后,对该表单的任何调用都会因此异常而失败。引发异常的原因很简单,因为表单分配的“UI线程”没有运行消息循环
请参阅Ivan的帖子,详细分析这是如何发生的:我最近也遇到了同样的问题。“我的表单”包含多个不可见的用户控件,这些控件可能需要在应用程序生命周期的后期出现。有时,这些请求来自后台线程
问题是,即使我将control.Visible=true
括在控件.Invoke
中,控件实际上被分配给了后台线程(如Chris的第3点所述),而不是窗体的主UI线程。对我来说,一个简单的解决方法是在创建父窗体期间(例如从窗体的加载事件)调用一次IWin32Window.Handle
属性,这样可以确保控件在主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组件上的代码时,就会发生这种情况
这通常发生在用户关闭已由不同线程更新的窗口(表单)时,并且在更新频率较高时更为普遍(因为用户关闭表单时发生不完整调用的可能性较高)
这可以通过以下方式妥善处理:
设置标志以指示正在进行调用
截获用户界面或返回
停止进一步(新)调用以避免发生
等待现有调用完成
完成预定的处置或归还
下面的示例显示了如何优雅地处理最常见的场景(当表单在更新时关闭)
该示例来自一个简单表单,该表单具有一个列表框,该列表框通过公共方法(AddItem(string))从外部线程更新
旗帜
调用代码
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());
}
static void Main()
{
Form1 frm=新Form1();
Task.Factory.StartNew(()=>frm.ShowDialog(),TaskCreationOptions.LongRunning);
int i=0;
而(i<2000)
{
if(frm.ShuttingDown!=true)//还可以通知客户端并允许其处理UI中断
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());
}