2010-02-21 20 views
10

Tengo un formulario de Windows con un ListView en modo de informe. Para cada elemento en la vista, necesito realizar una operación de larga ejecución, cuyo resultado es un número.¿Cuál es el equivalente de C# de MsgWaitForMultipleObjects?

La forma en que haría esto en win32 nativo es crear un hilo de trabajo para cada elemento (ingenuamente, por supuesto no crearé un número ilimitado de subprocesos) y luego MsgWaitForMultipleObjects() en la matriz de manejadores de subprocesos. A medida que finaliza cada cálculo, los hilos señalan y el subproceso principal de UI se activa y se actualiza. Mientras tanto, enviamos mensajes para que el hilo de la interfaz de usuario siga siendo receptivo.

¿Alguien puede dar un ejemplo de cómo esto podría funcionar en C#? Miré el objeto Monitor, y no parece ser lo que quiero, ¿o bombea mensajes mientras bloquea?

Gracias.

Editar: Parece que WaitHandler.WaitAny() podría realmente bombear mensajes. Consulte cbrumme's treatise sobre el bombeo de mensajes en el CLR.

+0

Tiene razón, Monitor no bombea mensajes. WaitHandle puede ser un mejor lugar para buscar, pero no pude encontrar un método WaitHandle que también generara mensajes. – itowlson

+0

Sí, también lo vi. Tampoco es un requisito que pueda esperar en múltiples objetos. Lo llevaré esperando un solo evento o algo ... Simplemente no quiero bloquear la interfaz de usuario. Todas las muestras que he encontrado hacen 'sleep (100)' o algo así para forzar un cambio de contexto, lo cual es muy triste. –

+0

Supongo que el problema es que en WinForms el ciclo de mensajes no es explícito. Entonces no puedes hacer un MsgWaitX directamente, sino que necesitas algo que genere un evento. Por ejemplo, ejecute cada subtarea como un BackgroundWorker (y olvídese de las primitivas de sincronización). – itowlson

Respuesta

2

El objeto activo de larga ejecución, creo, es la mejor opción en su caso. El hilo principal llama al proxy (del objeto activo). Proxy convierte el método de llamada al mensaje y este mensaje va a una cola. Proxy devuelve al llamante el objeto futuro (es una referencia al resultado futuro). El despachador dequeues mensajes uno por uno y realmente ejecuta su tarea en otro hilo (hilo de trabajo). Cuando el hilo de trabajo completa una tarea, actualiza el resultado del objeto futuro o llama al método de devolución de llamada (por ejemplo, para actualizar su UI). El detector puede tener muchos hilos de trabajo para ejecutar más una tarea al mismo tiempo.

Puede ver esto article (con muestra) sobre el patrón de objeto activo de larga ejecución.

+0

Proporcione información y no solo enlaces a información. –

+0

Hmm, eso realmente parece una gran cantidad de código para resolver un problema tan simple. Quiero decir, siempre podría pinvokear mi propio objeto COM si quisiera. Si el marco C# no está lo suficientemente maduro como para permitirme hacer esto fácilmente, puedo volver a escribir el código win32. Aunque tengo que creer que es una manera fácil de hacer esto. –

+0

Sí, tienes razón. Este es un ejemplo de MSDN: connection1.Open(); SqlCommand command1 = nuevo SqlCommand (commandText1, connection1); IAsyncResult result1 = command1.BeginExecuteNonQuery(); WaitHandle waitHandle1 = result1.AsyncWaitHandle; connection2.Open(); \t \t ... \t \t WaitHandle [] = {waitHandles waitHandle1, waitHandle2, waitHandle3 }; \t \t bool result = WaitHandle.WaitAll (waitHandles, 60000, false); – garik

2

Haga que su hilo principal cree una secuencia de administrador. Puede usar el BackgroundWorker para esto. Este hilo de administrador inicia un hilo de trabajo para cada elemento en el ListView. Esto permitirá que su UI siga respondiendo a la entrada del usuario sin colgar mientras se procesan los hilos de fondo.

Ahora, el problema es cómo esperar a que termine cada subproceso de trabajo. Lamentablemente, no he podido encontrar la forma de obtener un identificador de subproceso para objetos System.Threading.Thread. No digo que no haya una manera de hacerlo; Simplemente no he encontrado uno. Otro aspecto complicado de esto es que la clase System.Threading.Thread está sellada, por lo que no podemos derivar de ella para proporcionar algún tipo de 'manejo'.

Aquí es donde yo uso ManualResetEvent.

Digamos que cada subproceso de trabajo es simplemente un subproceso ThreadPool. La gestión BackgroundWorker crea un objeto ManualResetEvent para cada elemento en el ListView. Como el BackgroundWorker lanza cada hilo ThreadPool, pase ManualResetEvent como argumento al QueueUserWorkItem function. Luego, justo antes de que salga cada hilo ThreadPool, configure el objeto ManualResetEvent.

El subproceso BackgroundWorker puede colocar todos los objetos ManualResetEvent en una matriz y esperar en esa matriz con WaitHandle.WaitXXX functions. A medida que finaliza cada subproceso, puede utilizar los eventos BackgroundWorker para actualizar la interfaz de usuario o puede usar la técnica Control.Invoke() para actualizar la interfaz de usuario (consulte la respuesta de Marc Gravell here).

Espero que esto ayude.

+0

Esto parece un enfoque razonable si no puedo entender una espera de bombeo de mensajes. Encuentro realmente difícil creer que no hay uno. Actualizaré el hilo si encuentro algo. –

Cuestiones relacionadas