2010-02-21 7 views
10

Ho un Windows Form con ListView in modalità Report. Per ogni elemento nella vista, ho bisogno di eseguire un'operazione lunga, il cui risultato è un numero.Qual è l'equivalente C# di MsgWaitForMultipleObjects?

Il modo in cui vorrei farlo in Win32 nativo è creare un thread di lavoro per ciascun elemento (in modo ingenuo, ovviamente non creerò un numero illimitato di thread) e quindi MsgWaitForMultipleObjects() sulla matrice di handle di thread. Al termine di ogni calcolo, il segnale dei thread e il thread dell'interfaccia utente principale si sveglia e si aggiorna. Nel frattempo, pompiamo i messaggi in modo che il thread dell'interfaccia utente rimanga reattivo.

Qualcuno può fornire un esempio di come questo potrebbe funzionare in C#? Ho guardato l'oggetto Monitor, e non sembra essere quello che voglio, o pompa dei messaggi durante il blocco?

Grazie.

Modifica: Sembra che WaitHandler.WaitAny() potrebbe effettivamente pompare messaggi. Vedere cbrumme's treatise sul pompaggio dei messaggi nel CLR.

+0

Hai ragione, Monitor non pompa messaggi. WaitHandle potrebbe essere un posto migliore dove guardare, ma non sono riuscito a trovare un metodo WaitHandle che pompasse anche i messaggi. – itowlson

+0

Sì, l'ho visto anche io. Non è nemmeno un requisito che io possa aspettare su più oggetti. Prenderò in attesa di un singolo evento o qualcosa del genere ... semplicemente non voglio bloccare l'interfaccia utente. Tutti gli esempi che ho trovato fanno 'sleep (100)' o qualcosa per forzare un interruttore di contesto, che è molto triste. –

+0

Immagino che il problema sia che in WinForms il ciclo del messaggio non è esplicito. Quindi non puoi fare direttamente un MsgWaitX, hai invece bisogno di qualcosa che aumenti un evento. Ad esempio, esegui ogni attività secondaria come BackgroundWorker (e dimentica le primitive di sincronizzazione). – itowlson

risposta

2

L'oggetto attivo di lunga durata, penso, è la scelta migliore nel tuo caso. Il thread principale chiama il proxy (di oggetto attivo). Il proxy converte il metodo di chiamata nel messaggio e questo messaggio sta andando in coda. Il proxy restituisce al chiamante l'oggetto futuro (è un riferimento al risultato futuro). Il dispatcher rimuove i messaggi uno alla volta e esegue l'attività in altro thread (thread di lavoro). Quando il thread di lavoro completa un'attività, aggiorna il risultato dell'oggetto futuro o chiama il metodo callback (ad esempio, per aggiornare l'interfaccia utente). Il paziente può avere molti thread di lavoro per eseguire più attività contemporaneamente.

È possibile visualizzare questo article (con esempio) sul modello di oggetto attivo con esecuzione prolungata.

+0

Fornire alcune informazioni e non solo collegamenti a informazioni. –

+0

Hmm, sembra davvero un sacco di codice per risolvere un problema così semplice. Voglio dire, potrei sempre solo pinvoke il mio oggetto COM se volessi. Se il framework C# non è abbastanza maturo da consentirmi di farlo facilmente, posso semplicemente tornare a scrivere codice win32. Anche se devo credere che il loro è un modo semplice per farlo. –

+0

Sì, hai ragione. Questo è un esempio da MSDN: connection1.Open(); Comando SqlCommand1 = nuovo SqlCommand (comandoTesto1, connessione1); 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

Fare in modo che il thread principale crei un thread di gestione. Potresti usare lo BackgroundWorker per questo. Questo thread manager avvia un thread di lavoro per ciascun elemento in ListView. Ciò consentirà all'interfaccia utente di continuare a rispondere all'input dell'utente senza bloccarsi mentre i thread in background vengono elaborati.

Ora, il problema è come attendere il completamento di ogni thread di lavoro. Sfortunatamente, non sono stato in grado di trovare un modo per ottenere un handle di thread per gli oggetti System.Threading.Thread. Non sto dicendo che non c'è un modo per farlo; Non l'ho trovato. Un altro aspetto complicato di questo è che la classe System.Threading.Thread è sigillata, quindi non possiamo ricavarla per fornire una sorta di "handle".

Questo è dove io uso ManualResetEvent.

Diciamo che ogni thread di lavoro è semplicemente un thread ThreadPool. La gestione di BackgroundWorker crea un oggetto ManualResetEvent per ciascun elemento in ListView. Mentre lo BackgroundWorker avvia ciascuna thread ThreadPool, passare lo ManualResetEvent come argomento allo QueueUserWorkItem function. Quindi, appena prima che ogni thread ThreadPool esca, impostare l'oggetto ManualResetEvent.

Il thread BackgroundWorker può quindi inserire tutti gli oggetti ManualResetEvent in una matrice e attendere su tale array utilizzando WaitHandle.WaitXXX functions. Al termine di ogni thread, è possibile utilizzare gli eventi BackgroundWorker per aggiornare l'interfaccia utente oppure è possibile utilizzare la tecnica per aggiornare l'interfaccia utente (vedere la risposta di Marc Gravell here).

Spero che questo aiuti.

+0

Questo sembra un approccio ragionevole se non riesco a capire un messaggio di attesa del pompaggio. Trovo davvero difficile credere che non ce ne sia uno. Aggiornerò il thread se trovo qualcosa –