2009-02-10 3 views

risposta

17

http://blogs.msdn.com/cbrumme/archive/2003/07/14/51495.aspx

dice:

"Un fatto sorprendente è che questo è anche il motivo per Delegate.BeginInvoke/ EndInvoke sono così lento rispetto al tecniche equivalenti come ThreadPool.QueueUserWorkItem (o UnsafeQueueUserWorkItem se si utilizza per le implicazioni sulla sicurezza e si desidera essere veramente efficiente. Il codepath per BeginInv oke/EndInvoke si trasforma rapidamente nel codice di elaborazione del messaggio comune del percorso di remoting generale . "

+0

Hmmm, grazie, è interessante e molto spaventoso. Penso che ripristinerò qualsiasi codice BeginInvoke()/EndInvoke() che non richiede valori di ritorno! – endian

21

La cosa più importante che posso pensare con QueueUserWorkItem è che si deve utilizzare il tipo WaitCallback delegato, che guarda difficile se si dispone già di un'istanza SomeRandomDelegate e alcune args. La buona notizia è che si può risolvere questo problema con una chiusura:

ThreadPool.QueueUserWorkItem(
    delegate { someDelegate(arg1, arg2); } 
); 

Questo modello assicura anche che si ottiene una corretta tipizzazione forte al momento della compilazione (a differenza di superamento di un arg object stato a QueueUserWorkItem e la colata nel metodo di destinazione). Questo modello può essere utilizzato anche quando si chiama metodi direttamente:

ThreadPool.QueueUserWorkItem(
    delegate { SomeMethod(arg1, arg2); } 
); 

Ovviamente, senza un EndInvoke equivalente, anche voi non può ottenere un valore di ritorno di nuovo fuori a meno che non si chiama un metodo/generare un evento/etc alla fine del tuo metodo ... su una nota correlata, devi stare attento con exception handling.

+3

Incontrare questo scenario esatto è stata la prima volta che ho pensato a me stesso, "Wow, io amo chiusure." –

+0

Wow, questo è un suggerimento supremo. Sembra una semantica di boxing/unboxing, e immagino non più costoso, in un ampio schema di cose. –

+0

Oops, tecnicamente, dovrebbe essere "delegato (oggetto o)" o simile affinché il codice possa essere compilato, credo, poiché QueueUserWorkItem si aspetta una firma delegata che corrisponda a WaitCallback, ovvero una che include un singolo parametro di tipo oggetto. –

-1

Non ci dovrebbero essere grandi differenze, penso anche che BeginInvoke/EndInvoke generato per un delegato utilizzi il pool di thread da eseguire.

-1

Non ci dovrebbero essere differenze di prestazioni, poiché sia ​​Delegate.BeginInvoke che ThreadPool.QueueUserWorkItem verranno eseguiti su un thread pool di thread.

La più grande differenza è che se chiamate BeginInvoke, siete obbligati a chiamare EndInvoke ad un certo punto. Al contrario, ThreadPool.QueueUserWorkItem è "fire and forget". Ciò ha vantaggi e svantaggi. Il vantaggio è che puoi dimenticartene. Lo svantaggio è che non si ha modo di saperlo, a meno che non si aggiunga il proprio meccanismo di sincronizzazione/notifica, una volta completata l'attività.

14

L'EndInvoke() ha un comportamento utile ma raramente menzionato - è genera nuovamente tutte le eccezioni non gestite che il delegato generato nel contesto del thread originale in modo da poter spostare la logica di elaborazione delle eccezioni nel codice principale.

Inoltre, se il delegato ha parametri out/ref, verranno aggiunti alla firma EndInvoke() che consente di ottenerli quando il metodo termina l'esecuzione.

+0

molte grazie, non lo sapevo – endian

4

Se si chiama ThreadPool.QueueUserWorkItem, le eccezioni generate nell'oggetto di lavoro non saranno gestite sul thread in background (a meno che non vengano catturate in modo esplicito). In .Net 2 e successivi, questo terminerà l'AppDomain.

Se si chiama delegate.BeginInvoke(), le eccezioni vengono messe in coda per essere nuovamente generate quando viene chiamato EndInvoke(). Se non si chiama EndInvoke(), le eccezioni sono essenzialmente memoria "trapelata" (come qualsiasi altro stato non rilasciato dall'operazione asincrona).

Problemi correlati