Qual è la differenza tra QueueUserWorkItem () e BeginInvoke (), per eseguire un’attività asincrona senza necessità di tipi di ritorno

Seguendo la mia domanda BeginInvoke () / EndInvoke (), ci sono grandi differenze nelle prestazioni / nient’altro tra Delegate.BeginInvoke () e l’uso di QueueUserWorkItem () per richiamare un delegato in modo asincrono?

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

dice:

“Un fatto sorprendente è che questo è anche il motivo per cui Delegate.BeginInvoke / EndInvoke è così lento rispetto a tecniche equivalenti come ThreadPool.QueueUserWorkItem (o UnsafeQueueUserWorkItem se si comprendono le implicazioni di sicurezza e si vuole essere veramente efficienti). Il codepath per BeginInvoke / EndInvoke rapidamente si trasforma nel comune codice di elaborazione dei messaggi del percorso di remoting generale. ”

La cosa principale che riesco a pensare con QueueUserWorkItem è che devi usare il tipo di delegato WaitCallback , che sembra complicato se hai già un’istanza SomeRandomDelegate e alcuni argomenti. La buona notizia è che puoi risolvere il problema con una chiusura:

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

Questo modello garantisce inoltre di ottenere una corretta digitazione corretta in fase di compilazione (diversamente dal passare uno stato di object arg a QueueUserWorkItem e a lanciarlo nel metodo di destinazione). Questo modello può essere utilizzato anche quando si chiamano i metodi direttamente:

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

Ovviamente, senza un equivalente EndInvoke , non è ansible ottenere un valore di ritorno a meno che non si chiami un metodo / si alzi un evento / etc alla fine del metodo … su una nota correlata, è necessario fare attenzione con eccezione manipolazione .

EndInvoke () ha un comportamento utile ma raramente menzionato: ripianifica tutte le eccezioni non gestite generate dal delegato 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, saranno aggiunti alla firma EndInvoke () che consente di ottenerli quando il metodo termina l’esecuzione.

Se chiami ThreadPool.QueueUserWorkItem, le eccezioni generate nell’object di lavoro non verranno gestite nel thread in background (a meno che non vengano rilevate esplicitamente). 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).

Non dovrebbe esserci alcuna grande differenza, penso anche che BeginInvoke / EndInvoke generato per un delegato utilizzi il pool di thread da eseguire.

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

La più grande differenza è che se chiami BeginInvoke, sei obbligato 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à.