2009-07-07 7 views
6

He estado trabajando en el mismo proyecto ahora desde Navidad de 2008. Me han pedido que lo tome de una aplicación de consola (que simplemente imprime declaraciones de seguimiento), a un completo Aplicación de Windows. Claro, está bien. Lo único es que hay partes de la aplicación que pueden tardar de varios minutos a casi una hora en ejecutarse. Necesito multiprocesarlo para mostrar el estado del usuario o los errores. Pero no tengo idea de por dónde empezar.Multithreading: cómo actualizar la interfaz de usuario para indicar el progreso

He desarrollado una pequeña interfaz de usuario en WPF. Es muy básico, pero me gustaría expandirlo según sea necesario. La aplicación funciona seleccionando una fuente, eligiendo un destino y haciendo clic en iniciar. Me gustaría que se actualice un listbox a medida que avanza el proceso. De la misma manera que SQL Server se instala, cada paso tiene una marca de verificación verde por su nombre a medida que se completa.

¿Cómo comienza un principiante el subprocesamiento múltiple? ¿Qué bibliotecas debería consultar? Cualquier consejo sería muy apreciado.

p.s. Actualmente estoy leyendo acerca de esta biblioteca, http://www.codeplex.com/smartthreadpool

@ Martin: Aquí está cómo se construye mi aplicación:

  1. motor: Se ejecuta todos los componentes principales en orden predefinido
  2. Excel: Biblioteca escribí para envolver COM para abrir/leer/cierre/guardar libros
  3. Biblioteca: Biblioteca, que comprende diferentes tipos de formatos de libro (5 en total)
  4. Clases de actividad: Las clases que he escrito para traducir los datos de Excel y prepararlo para el Acceso
  5. Biblioteca DB: Una biblioteca que he escrito que utiliza ADO.NET para leer los datos de acceso
  6. AppSettings: se entiende la idea
  7. Serialier: Guardar los datos en caso de accidente aplicación

I utilice todo desde LINQ hasta ADO.NET para obtener datos, transformarlos y luego generarlos.

Mi principal requisito es que quiero actualizar mi interfaz de usuario para indicar el progreso

@Frank: ¿Qué pasa si algo en el fondo Trabajador produce una excepción (manejado o no)? ¿Cómo recibe mi solicitud un aviso?

@Eric Lippert: Sí, estoy investigando eso ahora mismo. Antes de complicar las cosas.

Avíseme si necesita más información. Actualmente estoy ejecutando esta aplicación desde una prueba de unidad, así que supongo que llamar a una aplicación de consola no es verdad. Yo uso Resharper para hacer esto. Soy la única persona en este momento que usa la aplicación, pero me gustaría tener una interfaz más atractiva

+4

Antes de ir, agregue un montón de complejidad de subprocesos múltiples, puede considerar cómo escribiría su aplicación para que responda al usuario sin dejar de ser un solo subproceso. Es razonable cambiar los algoritmos para que puedan hacer un poco de cálculo, devolver el hecho de que progresaron, dejar que la UI se actualice y luego reanudar el cálculo donde lo dejaron. –

+0

Si se trata de una API de base de datos que va a ser muy difícil.Los usuarios tendrán redes lentas, fallas de red, tiempos de espera de búsqueda de nombres. El DB estará ocupado a veces, etc. El manejo de la E/S de red sin procesar es posible en un modo no bloqueante/asíncrono. Pero pasar por ADO.NET, no es realmente una opción viable. – nos

+0

@noselasd: Solo estoy usando ADO.NET por un tiempo, no lo estoy usando como mi back-end store. – Chris

Respuesta

0

La mejor manera para que un recién llegado sea totalmente subprocesador es probablemente el grupo de subprocesos. Probablemente necesitemos saber un poco más sobre estas piezas para hacer recomendaciones más detalladas

EDITAR ::
Como ahora tenemos un poco más de información, voy a seguir con mi respuesta anterior, parece como si tuvieras muchas tareas que hacer, la mejor manera de hacer un montón de tareas es agregarlas al threadpool y luego seguir comprobando si están listas, si las tareas deben hacerse en un orden específico, entonces puede simplemente agregar el siguiente como termina el anterior.El threadpool realmente es bastante bueno para este tipo de cosas y no veo ninguna razón para no usarlo en este caso

6

No creo que especifiques la versión del CLR que estás usando, pero podrías echarle un vistazo al " BackgroundWorker "control. Es una forma sencilla de implementar múltiples hilos.

La mejor parte, es que es a part of the CLR 2.0 and up

actualización en respuesta a la actualización: Si usted quiere ser capaz de actualizar los avances en la interfaz de usuario - por ejemplo, en una barra de progreso - la el trabajador de fondo es perfecto. Utiliza un evento que creo que se llama: ProgressChanged para informar el estado. Es muy elegante. Además, tenga en cuenta que puede tener tantas instancias como necesite y puede ejecutar todas las instancias al mismo tiempo (si es necesario).

En respuesta a su pregunta: Puede configurar fácilmente un proyecto de ejemplo y probar su pregunta. Lo que encontrar los siguientes, here (el recuadro de comentarios, segundo párrafo de la cautela):

Si la operación genera una excepción que su código no maneja, la BackgroundWorker captura la excepción y lo pasa a la Controlador de eventos RunWorkerCompleted, donde está expuesto como el Error propiedad de System.ComponentModel .. ::. RunWorkerCompletedEventArgs.

+0

Creo que sí. Es solo una clase .net. –

+0

Gracias, lo leeré. – Chris

+0

Solo tenga en cuenta que, antes de saltar al multihilo a través del trabajador en segundo plano, debe estar al tanto de la seguridad del hilo. TIENE que saber que acceder y actualizar datos en un hilo desde otro hilo es peligroso e inseguro. Otros enlaces aquí le presentan el bloqueo y similar que se necesita. – nos

0

This page es un buen resumen de la rosca.

Por lo que parece, probablemente no necesite nada muy complejo: si solo comienza la tarea y luego quiere saber cuándo ha terminado, solo necesita unas pocas líneas de código para crear un nuevo hilo y obtener para ejecutar tu tarea. Luego, su subproceso de interfaz de usuario puede seguir y verificar periódicamente si la tarea se ha completado.

+0

@Jason: Es un poco más que eso. Estoy procesando más de 5000 libros de trabajo y me gustaría saber qué libro de trabajo se está procesando, etc., ya que la interfaz de usuario se encuentra allí. – Chris

+0

Si sus necesidades son simples, puede usar volátiles para pasar valores entre hilos con un esfuerzo mínimo. p.ej. si tiene una lista de libros de trabajo, puede indicar el progreso utilizando un int que indique el índice del libro de trabajo actual. Al declararlo como "volátil int myIndex"; no necesita usar bloqueos para acceder de manera segura entre hilos. En el hilo de la interfaz de usuario, puede agregar un Formulario. Temporizador que lo llama (por ejemplo) un par de veces por segundo y puede actualizar la interfaz de usuario simplemente leyendo myIndex e informándola. Si necesita datos más complejos, colóquelos en una clase y use el bloqueo de todos los accesos. –

+0

Tenga en cuenta que los volátiles solo se pueden usar con algunos tipos (como bool, int) –

0

Concurrent Programming on Windows es EL mejor libro de la existencia sobre el tema. Escrito por Joe Duffy, famoso gurú de Microsoft de multihilo. Todo lo que necesita saber y más, desde la forma en que el programador de subprocesos de Windows funciona hasta la Biblioteca de extensiones .NET Parallels.

+0

¿Cómo comienza un principiante el subprocesamiento múltiple? Dudo con este libro. – Nick

0

Recuerde que crear sus delegados para actualizar la interfaz de usuario para que no se consigue cuestiones la rosca y la interfaz de usuario no aparece para congelar/bloqueo

Además, si usted necesita una gran cantidad de puntos de notas/potencia/etc, etc

¿Puedo sugerir todas las notas de la conferencia de mi licenciatura http://ist.psu.edu/courses/SP04/ist411/lectures.html

0

enlace de Jason es un buen artículo. Las cosas que debe tener en cuenta son que la interfaz de usuario solo se puede actualizar mediante el subproceso principal de la interfaz de usuario, obtendrá excepciones de subprocesamiento cruzado si intenta hacerlo en el subproceso de trabajo. El control BackgroundWorker puede ayudarlo con los eventos, pero también debe conocer Control.Invoke (o Control.Begin/EndInvoke). Esto se puede usar para ejecutar delegados en el contexto del hilo de UI.

También debe leer sobre las dificultades para acceder al mismo código/variables de diferentes hilos, algunos de estos problemas pueden dar lugar a errores que son intermitentes y difíciles de rastrear.

Un punto a tener en cuenta es que la palabra clave volátil solo garantiza la "frescura" del acceso variable, por ejemplo, garantiza que cada lectura y escritura de la variable será desde la memoria principal, y no desde un subproceso o caché de procesador o otra 'característica' del modelo de memoria. No detiene problemas como un hilo interrumpido por otro hilo durante su proceso de lectura-actualización-escritura (por ejemplo, cambiando el valor de las variables). Esto causa errores cuando los 2 hilos tienen valores diferentes (o los mismos) para la variable, y pueden hacer que se pierdan cosas como valores, 2 hilos que tengan el mismo valor para la variable cuando deberían tener valores diferentes, etc. Debería usar un bloqueo/monitor (u otro método de sincronización de subprocesos, identificadores de espera, incrustaciones/reducciones entrelazadas, etc.) para evitar este tipo de problemas, que garantizan que solo un subproceso pueda acceder a la variable. (El monitor también tiene la ventaja de que realiza lecturas/escrituras volátiles implícitamente)

Y como alguien más ha notado, también debe intentar evitar bloquear el subproceso de la interfaz de usuario mientras espera que se completen los subprocesos, de lo contrario su UI no responderá . Puede hacer que sus subprocesos de trabajo generen eventos a los que su UI se suscribe indica progreso o finalización.

Matt

0

Typemock tienen una nueva herramienta llamada Racer para ayudar con problemas de subprocesos múltiples. Es un poco avanzado pero puedes obtener ayuda en su foro y en otros foros en línea (uno que extrañamente me viene a la mente es stackoverflow :-))

0

Soy un novato en el multihilo también, pero estoy de acuerdo con Frank en que un trabajador de base es probablemente su mejor opción. Funciona a través de suscripciones de eventos. Estos son los conceptos básicos de cómo lo usaste.

  • Primera instancia de un nuevo trabajador fondo
  • métodos suscritos en el código para los trabajadores de fondo grandes eventos:
    • DoWork: Este debe contener cualquier código que toma mucho tiempo para procesar
    • ProgressChanged : Esto se envuelve cada vez que se llama a ReportProgress() desde el método suscrito a DoWork
    • RunWorkerCompleted: Envoked cuando el método DoWork ha completado

Cuando esté listo para ejecutar su proceso que lleva tiempo se llama al método RunAsync() del trabajador de fondo. Esto inicia el método DoWork en un hilo separado, que luego puede informar su progreso a través del evento ProgressChanged. Una vez que complete RunWorkerComplete será evocado.

El método de evento DoWork también puede verificar si el usuario de alguna manera solicitó que se cancelara el proceso (se llamó CanceLAsync()) al verificar el valor de la propiedad CancelPending.

Cuestiones relacionadas