了解 InvalidAsynchronousStateException 的发生

发布于 2024-11-15 17:26:26 字数 1477 浏览 1 评论 0原文

何时抛出 InvalidAsynchronousStateException?

我有以下代码:

control.InvokeRequired ? 控制.调用(表达式): 表达式();

在某些随机情况下,我得到 InvalidAsynchronousStateException 并且我的应用程序挂起,经过一些阅读后,似乎在创建 control 的线程完成时会抛出此异常。这是正确的吗?如果是这样,情况似乎并非如此,除非有什么原因导致我的应用程序崩溃并且此异常只是一个结果?这可能吗?


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


顺便说一句,我已经检查了这个答案,但没有澄清我的疑问 -> 检查控制是否需要调用的函数中的 InvalidAsynchronousStateException< /a>

When does InvalidAsynchronousStateException get thrown?

I have the following piece of code:

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

In some random cases I get InvalidAsynchronousStateException and my application hangs, after doing some reading it seems to be that this exception will be thrown when the thread where the control was created finished. Is this correct? If so, this doesn't seem to be the case, unless something is making my application crash and this exception is just a consequence? is this possible?


System.ComponentModel.InvalidAsynchronousStateException: An error occurred invoking the method. The destination thread no longer exists.
at System.Windows.Forms.Control.WaitForWaitHandle(WaitHandle waitHandle)
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.Invoke(Delegate method)
at Optimus.Desktop.Framework.Spring.Aspects.UIThreadInterceptor.Invoke(IMethodInvocation invocation) in c:\Optimus\Desktop\Framework\Spring\Aspects\UIThreadInterceptor.cs:line 22
at Spring.Aop.Framework.AbstractMethodInvocation.Proceed()
at Spring.Aop.Framework.DynamicProxy.AdvisedProxy.Invoke(Object proxy, Object target, Type targetType, MethodInfo targetMethod, MethodInfo proxyMethod, Object[] args, IList interceptors)
at InheritanceAopProxy_4fda07e8828744839065a154b30915ee.Dispose(Boolean disposing)
at System.ComponentModel.Component.Finalize()


btw, I've checked this answer and didn't clarify my doubt -> InvalidAsynchronousStateException in function that checks if invoke is required for control

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

星星的軌跡 2024-11-22 17:26:26

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

背景如下:

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

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

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

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

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

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

Usually this occurs when a background thread is attempting to invoke to a UI thread after the UI thread has already exited. Do you by any chance attempt to run different forms each in their own thread, or do you Show() forms from a non-UI thread, or Invoke() to a form before it is shown?

The background is as follows:

1) Every control (including Forms) has a handle. This is used to tie the control back to the underlying windows GDI objects.

2) The control's handle is usually not created when the control itself is created. The handle is created when the control is Show()n for the first time.

3) When Invoking to a control, the .NET API attempts to locate the control's UI thread using it's handle. If the form has not yet been shown, the CURRENT THREAD (the invoking thread) will be assigned as the UI thread.

4) The UI thread for a control is expected to run a message loop for handling that control (which happens automatically when you do, for instance, Application.Run(someForm);

5) So the common mistake is that you create a form F, Invoke() or BeginInvoke() to it from a temporary or threadpool thread, which creates the form's handle and is therefore assigned as the form's UI thread. Then the background thread exits, or is terminated by the threadpool, or simply fails to run a message loop, since it is not aware that it has been designated a UI thread. Subsequently, any invocations to that form fail with this exception. The exception is thrown simply because the form's assigned 'UI thread' is not running a message loop.

See Ivan's post for a detailed analysis of how this happens: http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html

没有心的人 2024-11-22 17:26:26

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

问题是,即使我将 control.Visible = true 包含在 control.Invoke 中,该控件实际上还是分配给了后台线程(如 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; 
  }
}

I've been faced to the same issue recently. My form contains several invisible user controls that may be required to appear later in the life cycle of the application. Sometimes, those requests come from background threads.

The problem was that even if I enclose control.Visible = true inside a control.Invoke, the control was actually assigned to the background thread (as mentioned in Chris's point #3) instead of the form's main UI thread. A simple workaround for me was to call once the IWin32Window.Handle property during the creation of the parent form (for instance from the form's Load event) This ensures that the control is created in main UI thread without making it visible.

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; 
  }
}
成熟稳重的好男人 2024-11-22 17:26:26

正如其他人正确地表明的那样,当 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

As others have correctly shown this happens when a UI component is disposed or is completed (return) while a different thread is still invoking code on the same UI component.

This usually happens when a user closes a window (a form) that has been updated by a different thread and this is more prevalent when the update frequency is high (because the chance of having an incomplete invoke when the user closes the form is high).

This can be gracefully handled by:

  1. Set a flag to indicate an invoke is in progress
  2. Intercept the UI dispose or return
  3. Stop further (new) invokes from taking place
  4. Wait until existing invokes finish
  5. Complete the intended dispose or return

Below example shows how to gracefully handle the most common scenario (when a form is closed while it's been updated).

The example is from a simple form that has a listbox that is updated from an outside thread via a public method (AddItem(string)).

Flags

private bool invokeInProgress = false;
private bool stopInvoking = false

Invoking code

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

Intercepting and managing form closing event

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

You can further extend this by exposing a method or a property that lets the users (other threads) know that the form is shutting down so that the callers can gracefully handle the situation.

Example below shows how a new property (ShuttingDown) allows a caller to handle its flow correctly if a user closes the display form.

New flag on form

public bool ShuttingDown { get { return stopInvoking; } }

Caller now can detect the problem

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

You can read more and download the sample project from here: http://www.ilearnttoday.com/c-sharp-the-destination-thread-no-longer-exists

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文