example - threadpool c# ejemplo
entender las ocurrencias de InvalidAsynchronousStateException (3)
Como otros han demostrado correctamente, esto sucede cuando un componente de la interfaz de usuario se elimina o se completa (devuelve), mientras que un subproceso diferente sigue invocando el código en el mismo componente de la interfaz de usuario.
Esto suele suceder cuando un usuario cierra una ventana (un formulario) que ha sido actualizada por un subproceso diferente y esto es más frecuente cuando la frecuencia de actualización es alta (debido a que la posibilidad de tener una invocación incompleta cuando el usuario cierra el formulario es alta) .
Esto puede ser manejado con gracia por:
- Establecer una bandera para indicar que una invocación está en progreso
- Interceptar la UI disponer o devolver
- Deja de más (nuevo) invocaciones de tener lugar
- Espera hasta que finalice la invocación existente.
- Completa la disposición o devolución prevista.
El siguiente ejemplo muestra cómo manejar con gracia el escenario más común (cuando un formulario se cierra mientras se actualiza).
El ejemplo es de un formulario simple que tiene un cuadro de lista que se actualiza desde un hilo externo a través de un método público (AddItem (cadena)).
Banderas
private bool invokeInProgress = false;
private bool stopInvoking = false
Invocando código
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();
}
Interceptar y gestionar el evento de cierre de formularios.
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();
}
}
Puede ampliar esto exponiendo un método o una propiedad que permite a los usuarios (otros subprocesos) saber que el formulario se está cerrando para que las personas que llaman puedan manejar la situación con gracia.
El siguiente ejemplo muestra cómo una nueva propiedad (ShuttingDown) le permite a la persona que llama manejar su flujo correctamente si un usuario cierra el formulario de visualización.
Nueva bandera en forma
public bool ShuttingDown { get { return stopInvoking; } }
El que llama ahora puede detectar el problema
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());
}
Puede leer más y descargar el proyecto de muestra desde aquí: http://www.ilearnttoday.com/c-sharp-the-destination-thread-no-longer-exists
¿Cuándo se lanza la excepción InvalidAsynchronousStateException?
Tengo el siguiente código:
control.InvokeRequired? control.Invoke (expresión): expresión ();
En algunos casos aleatorios obtengo InvalidAsynchronousStateException y mi aplicación se bloquea, después de leer un poco parece ser que esta excepción se lanzará cuando finalice el subproceso donde se creó el control
. ¿Es esto correcto? Si es así, este no parece ser el caso, a menos que algo esté bloqueando mi aplicación y esta excepción sea solo una consecuencia. ¿es posible?
System.ComponentModel.InvalidAsynchronousStateException: se produjo un error al invocar el método. El hilo de destino ya no existe. at System.Windows.Forms.Control.WaitForWaitHandle (WaitHandle waitHandle) en System.Windows.Forms.Control.MarshaledInvoke (Control caller, Delegate method, Object [] args, Boolean synchronous) at System.Windows.Forms.Control.Invoke Método de delegado, Objeto [] args) en System.Windows.Forms.Control.Invoke (Método de delegado) en Optimus.Desktop.Framework.Spring.Aspects.UIThreadInterceptor.Invoke (Invocación de ImethodInvocation) en c: / Optimus / Desktop / Framework / Spring / Aspects / UIThreadInterceptor.cs: línea 22 en Spring.Aop.Framework.AbstractMethodInvocation.Proceed () en Spring.Aop.Framework.DynamicProxy.AdvisedProxy.Invoke (Proxy de objeto, Objeto objetivo, Tipo targetType, MethodInfo targetMethod, MethodInfo ProxyMethod Objeto [] args, interceptores IList) en HerenciaAopProxy_4fda07e8828744839065a154b30915ee.Dispose (disposición booleana) en System.ComponentModel.Component.Finalize ()
Por cierto, he comprobado esta respuesta y no aclaré mi duda -> InvalidAsynchronousStateException en la función que comprueba si se requiere invocación para el control
Me he enfrentado al mismo problema recientemente. Mi formulario contiene varios controles de usuario invisibles que pueden aparecer más adelante en el ciclo de vida de la aplicación. A veces, esas solicitudes provienen de hilos de fondo.
El problema fue que incluso si encierro control.Visible = true
dentro de un control.Invoke
, el control se asignó realmente al subproceso de fondo (como se menciona en el punto # 3 de Chris) en lugar del subproceso principal de la interfaz de usuario del formulario. Una solución simple para mí fue llamar una vez la propiedad IWin32Window.Handle
durante la creación del formulario principal (por ejemplo, desde el evento de carga del formulario). Esto garantiza que el control se crea en el subproceso principal de la interfaz de usuario sin que sea 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;
}
}
Por lo general, esto ocurre cuando un subproceso en segundo plano está intentando invocar a un subproceso de la interfaz de usuario después de que el subproceso de la interfaz de usuario ya ha salido. ¿Es posible que intente ejecutar diferentes formularios cada uno en su propio hilo, o muestre los formularios () desde un hilo que no sea de la interfaz de usuario, o invoque () a un formulario antes de que se muestre?
El fondo es el siguiente:
1) Cada control (incluidas las formas) tiene un controlador. Esto se utiliza para vincular el control a los objetos GDI de Windows subyacentes.
2) El control del control generalmente no se crea cuando se crea el control. El identificador se crea cuando el control es Show () n por primera vez.
3) Cuando se invoca a un control, la API de .NET intenta localizar el subproceso de la interfaz de usuario del control utilizando su identificador. Si el formulario aún no se ha mostrado, el HILO ACTUAL (el hilo que invoca) se asignará como el hilo de la interfaz de usuario .
4) Se espera que el subproceso de la interfaz de usuario de un control ejecute un bucle de mensajes para manejar ese control (lo que sucede automáticamente cuando, por ejemplo, Application.Run (someForm);
5) Por lo tanto, el error común es que creas un formulario F, Invoke () o BeginInvoke () a partir de un hilo temporal o de agrupación de hilos, que crea el identificador del formulario y, por lo tanto, se asigna como el hilo de la interfaz de usuario del formulario. Luego, el subproceso de fondo se cierra, o se termina por el conjunto de subprocesos, o simplemente no ejecuta un bucle de mensajes, ya que no es consciente de que se ha designado un subproceso de interfaz de usuario. Posteriormente, cualquier invocación a ese formulario fallará con esta excepción. La excepción se produce simplemente porque el ''subproceso de la interfaz de usuario'' asignado al formulario no está ejecutando un bucle de mensajes.
Consulte la publicación de Ivan para obtener un análisis detallado de cómo sucede esto: http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html