понимание исключений InvalidAsynchronousStateException - PullRequest
21 голосов
/ 15 июня 2011

Когда генерируется InvalidAsynchronousStateException?

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

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

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


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


кстати, но я не проверял,проясните мои сомнения -> InvalidAsynchronousStateException вфункция, которая проверяет, требуется ли вызов для управления

Ответы [ 3 ]

32 голосов
/ 15 июня 2011

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

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

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

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

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

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

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

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

3 голосов
/ 25 августа 2011

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

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

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; 
  }
}
1 голос
/ 08 ноября 2016

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

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

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

  1. Установить флаг, чтобы указать, что выполняется вызов
  2. Перехватить удаление или возврат пользовательского интерфейса
  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

...