Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 了解InvalidAsynchronousStateException发生情况_C#_Winforms_Multithreading_Threadpool - Fatal编程技术网

C# 了解InvalidAsynchronousStateException发生情况

C# 了解InvalidAsynchronousStateException发生情况,c#,winforms,multithreading,threadpool,C#,Winforms,Multithreading,Threadpool,何时抛出InvalidAsynchronousStateException 我有以下代码: control.InvokeRequired? control.Invoke(表达式): 表达式() 在一些随机情况下,我会得到InvalidAsynchronousStateException,并且我的应用程序挂起,在进行一些读取之后,当创建控件的线程完成时,似乎会抛出此异常。这是正确的吗?如果是这样的话,情况似乎并非如此,除非有什么东西使我的应用程序崩溃,而这个异常只是一个结果?这可能吗 Syste

何时抛出InvalidAsynchronousStateException

我有以下代码:

control.InvokeRequired? control.Invoke(表达式): 表达式()

在一些随机情况下,我会得到InvalidAsynchronousStateException,并且我的应用程序挂起,在进行一些读取之后,当创建
控件的线程完成时,似乎会抛出此异常。这是正确的吗?如果是这样的话,情况似乎并非如此,除非有什么东西使我的应用程序崩溃,而这个异常只是一个结果?这可能吗


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());
    }