Подтвердить что ты не робот

Понимание ошибок InvalidAsynchronousStateException

Когда вызывается InvalidAsynchronousStateException?

У меня есть следующий фрагмент кода:

control.InvokeRequired? control.Invoke(выражение): выражение();

В некоторых случайных случаях я получаю InvalidAsynchronousStateException и мое приложение зависает, после некоторого чтения кажется, что это исключение будет вызываться, когда поток, в котором был создан control, был завершен. Это верно? Если это так, похоже, это не так, если только что-то не приводит к сбою моего приложения, и это исключение является следствием? возможно ли это?


System.ComponentModel.InvalidAsynchronousStateException: возникла ошибка при вызове метода. Целевой поток больше не существует.    в System.Windows.Forms.Control.WaitForWaitHandle(WaitHandle waitHandle)    в System.Windows.Forms.Control.MarshaledInvoke(контрольный вызов, метод делегирования, объект [] args, логический синхронный)    в System.Windows.Forms.Control.Invoke(метод Delegate, Object [] args)    в System.Windows.Forms.Control.Invoke(метод делегата)    в Optimus.Desktop.Framework.Spring.Aspects.UIThreadInterceptor.Invoke(вызов IMethodInvocation) в c:\Optimus\Desktop\Framework\ Spring\Aspects\UIThreadInterceptor.cs: строка 22    в Spring.Aop.Framework.AbstractMethodInvocation.Proceed()    at Spring.Aop.Framework.DynamicProxy.AdvisedProxy.Invoke(объект-прокси, объект-объект, тип targetType, метод-метод targetMethod, метод MethodInfo proxyMethod, Object [] args, перехватчики IList)    на InheritanceAopProxy_4fda07e8828744839065a154b30915ee.Dispose(Boolean disposing)    в System.ComponentModel.Component.Finalize()


btw, я проверил этот ответ и не уточнил свое сомнение → InvalidAsynchronousStateException в функции, которая проверяет, требуется ли для контроля.

4b9b3361

Ответ 1

Обычно это происходит, когда фоновый поток пытается вызвать в поток пользовательского интерфейса после того, как поток пользовательского интерфейса уже вышел. Вы случайно пытаетесь запускать разные формы в своем потоке или делаете ли вы Show() из потока, отличного от UI, или Invoke() до формы до его отображения?

Фон выглядит следующим образом:

1) Каждый элемент управления (включая формы) имеет ручку. Это используется для привязки элемента управления к основным объектам GDI окна.

2) Управляющий дескриптор обычно не создается при создании самого элемента управления. Ручка создается, когда элемент управления имеет значение Show() n в первый раз.

3) При вызове в элемент управления API.NET пытается найти поток пользовательского интерфейса управления с помощью дескриптора. Если форма еще не показана, ТЕКУЩАЯ НИТЬ (вызывающий поток) будет назначаться как поток пользовательского интерфейса.

4) Ожидается, что поток пользовательского интерфейса для элемента управления будет запускать цикл сообщений для управления этим элементом управления (что происходит автоматически, когда вы это делаете, например Application.Run(someForm);

5) Таким образом, распространенная ошибка заключается в том, что вы создаете для него форму F, Invoke() или BeginInvoke() из временного потока или потока threadpool, который создает дескриптор формы и поэтому назначается как поток пользовательского интерфейса формы. Затем фоновый поток выходит из строя или заканчивается threadpool или просто не запускает цикл сообщения, поскольку он не знает, что он был назначен потоком пользовательского интерфейса. Впоследствии любые вызовы в эту форму не выполняются с этим исключением. Исключение выбрано просто потому, что форма, назначенная "потоком пользовательского интерфейса", не запускает цикл сообщений.

См. сообщение Ivan для подробного анализа того, как это происходит: http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html

Ответ 2

В последнее время я столкнулся с той же проблемой. Моя форма содержит несколько невидимых пользовательских элементов управления, которые могут потребоваться для отображения позже в жизненном цикле приложения. Иногда эти запросы поступают из фоновых потоков.

Проблема заключалась в том, что даже если я заключу control.Visible = true внутри a control.Invoke, управление было фактически назначено фоновому потоку (как указано в пункте Криса № 3) вместо основного потока пользовательского интерфейса. Простым обходным решением для меня было вызывать однократно свойство IWin32Window.Handle при создании родительской формы (например, из формы Load event). Это гарантирует, что элемент управления создается в основном потоке пользовательского интерфейса, не делая его видимым.

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

Ответ 3

Как показали другие, это происходит, когда компонент пользовательского интерфейса расположен или завершен (возвращен), в то время как другой поток все еще вызывает код в одном компоненте пользовательского интерфейса.

Обычно это происходит, когда пользователь закрывает окно (форму), которое было обновлено другим потоком, и это более распространено, когда частота обновления высока (поскольку вероятность неполного вызова, когда пользователь закрывает форму высока).

Это может быть изящно обработано:

  • Установите флаг, указывающий, что вызов выполняется.
  • Перехватить пользовательский интерфейс или вернуть его
  • Остановить дальнейшие (новые) вызовы
  • Подождите, пока существующие вызовы завершатся
  • Завершить планируемое удаление или возврат

Ниже приведен пример того, как изящно обрабатывать наиболее распространенный сценарий (когда форма закрывается, когда она обновляется).

Пример из простой формы, которая имеет список, который обновляется из внешнего потока через общедоступный метод (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