2009-06-24 14 views
7

I tienen una especie de modelo de plug-in en el que los diversos controles de usuario complejas se almacenan en archivos DLL y cargado y instanciados en tiempo de ejecución utilizandocontroles creando en un no-UI hilo

Activator.CreateInstanceFrom(dllpath, classname). 

Desde que estoy loading bastantes de estos quería hacerlo en segundo plano, manteniendo mi IU sensible, creando un nuevo hilo para hacer la carga. Los controles se emparejan al formulario principal y se muestran cuando es necesario.

Parece que funciona bien, hasta que intento establecer cualquier propiedad en cualquier control anidado en uno de estos controles de usuario, p. Ej. en el controlador de eventos de un botón, que arroja una excepción de cruce cruzado. Me doy cuenta de que puedo evitar esto marcando InvokeRequired cada vez que accedo a una propiedad, pero prefiero no tener que preocuparme por eso al escribir el código para los controles de usuario (especialmente porque hay otros que también escriben estos bits de código que podrían no siempre recordar).

Así que mi pregunta es, ¿hay alguna manera segura de hacer lo que estoy intentando, o cómo debo cargar estos controles en segundo plano? ¿O es básicamente imposible y tengo que seguir el hilo principal para crear controles?

Espero que la información que he proporcionado sea suficiente para aclarar mi situación; si no, me complacería elaborar y proporcionar ejemplos de código.

+0

"hasta que intento establecer cualquier propiedad": ¿está en el hilo o más adelante? –

+0

Eso es más tarde, después de que el control de usuario ha sido emparentado con el formulario principal (como en form.controls.add (mycontrol)) –

Respuesta

3

Está bien cargar los archivos DLL y crear los objetos de control en segundo plano, pero el control debe agregarse al formulario en el hilo principal y toda la interacción del usuario, así como cualquier cambio programático de propiedades de control (después de que creado) tiene que ocurrir en el hilo principal. Simplemente no hay forma de evitar esto, ya que probablemente ya lo sepas. Ahora, a menos que la carga de esos archivos DLL y la creación de controles requiera años, no veo ninguna razón para hacerlo en hilos de fondo separados.

Si ciertas acciones realizadas por los controles están bloqueando la IU y desea que ocurran en segundo plano, esta es una historia diferente, y es mejor que considere cada acción de forma individual y cree métodos explícitos para comunicarse entre ellas y los hilos de fondo.

El intento de hacer uno genérico que sirva para todos marco que funciona de la misma en el modo de hilo de interfaz de usuario y en modo de fondo y simplemente comprueba los resultados InvokeRequired a veces en peor rendimiento (como todos los hilos se bloquean en la invocación) o incluso en interbloqueos (no detectados) tan pronto como la aplicación alcance una complejidad razonable. También realizar todas las actualizaciones asincrónicas en BeginInvoke, sin considerar cada método individualmente, puede dar como resultado problemas de consistencia de los datos (es decir, los controles pueden actualizarse a los estados atrás a tiempo debido a la inversión del orden de invocación entre subprocesos).

+0

Sí, tengo la sensación de que estaba tratando de ser demasiado inteligente. He abandonado más o menos mi primer intento y me estoy moviendo a lo largo de líneas similares a su segundo párrafo. Gracias por los consejos en par. 3; No me había dado cuenta de eso. –

+0

Personalmente, prefiero usar una cola (http://msdn.microsoft.com/en-us/library/7977ey2c.aspx) para comunicarme entre los hilos: el hilo de fondo agrega elementos ('resultados') a la cola, el El hilo de la interfaz de usuario comprueba la cola en reposo y actualiza los controles (esto es típico productor/consumidor). Pero debe considerar cuidadosamente si el fondo no puede superar la capacidad de la IU de mantenerse al día, lo que puede suceder fácilmente, especialmente con las redes, haciendo que la cola crezca fuera de control a menos que tome medidas especiales. –

1

El ejemplo de código en this answer proporciona una forma elegante de solucionar este problema.

Código citado de esa respuesta:

public void UpdateTestBox(string newText) 
{ 
    BeginInvoke((MethodInvoker) delegate { 
     tb_output.Text = newText; 
    });   
} 

... aunque en su caso que se quiere llamar BeginInvoke en los propios controles:

public void UpdateTestBox(string newText) 
{ 
    tb_output.BeginInvoke((MethodInvoker) delegate { 
     tb_output.Text = newText; 
    });   
} 
+0

Todavía no veo cómo esto ayudará. Quiero decir, sí, tienes razón, por supuesto, usar Invoke/BeginInvoke ayudará, pero eso es exactamente lo que estoy tratando de evitar. O tal vez simplemente no he entendido a qué te refieres. –

+1

Si crea los controles en un subproceso diferente al subproceso de la interfaz de usuario, no puedo ver cómo saldrá de Invoke/BeginInvoke. Sin embargo, el código sugerido es mucho más simple que la variante if (ctl.InvokeRequired). El castigo inmediato de ir multiproceso es que necesita hacer su sincronización;) –

1

No saber los detalles del mecanismo de InvokeRequired , He estado experimentando un poco y afaik puede establecer la mayoría de las propiedades en un subproceso, siempre y cuando no haya sido emparentado (es decir, agregado a alguna propiedad Control.Controles).

Así que debería poder preparar sus controles, almacenarlos en una lista y adjuntarlos a la interfaz de usuario principal en un método invocado.


Editar: No creo que importe qué hilo ha creado un Control. Por lo tanto, deberían aplicarse las reglas normales, es decir, solo puede trabajar con controles del hilo principal de Windows. Y creo que el criterio es HandleCreated en lugar de Parented. Mientras eso no haya sucedido, obtendrás un pequeño respiro.

+0

Sí, parece que la crianza es lo que me está matando. Su idea no es tan mala, pero no ayudará aquí, ya que las propiedades que estoy configurando son todas en respuesta a alguna interacción del usuario, en otras palabras * después * el control ha sido emparentado al formulario principal y se muestra. Eso puede no haber sido muy claro a partir de mi pregunta. –

+0

Estaba claro, pero estoy haciendo eso sin problemas. –

Cuestiones relacionadas