Какая разница между QueueUserWorkItem() и BeginInvoke(), для выполнения асинхронной активности без необходимости возврата типов

После моего вопроса BeginInvoke()/EndInvoke() существуют ли существенные различия в производительности/что-то еще между Delegate.BeginInvoke() и использованием QueueUserWorkItem() для асинхронного вызова делегата?

Ответ 1

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

говорит:

"Один удивительный факт заключается в том, что это также почему Delegate.BeginInvoke/ EndInvoke настолько медленны по сравнению с эквивалентные методы, такие как ThreadPool.QueueUserWorkItem(или UnsafeQueueUserWorkItem, если вы понимать последствия для безопасности и хотите быть действительно эффективными). codepath для BeginInvoke/EndInvoke быстро превращается в общее сообщение код обработки общего удаленный путь".

Ответ 2

Главное, с чем я могу думать, QueueUserWorkItem заключается в том, что вы должны использовать тип делегата WaitCallback, который выглядит сложным, если у вас уже есть экземпляр SomeRandomDelegate и некоторые аргументы. Хорошей новостью является то, что вы можете исправить это с закрытием:

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

Этот шаблон также гарантирует, что вы получите правильную сильную типизацию во время компиляции (в отличие от передачи состояния object arg QueueUserWorkItem и его литья в целевом методе). Этот шаблон также можно использовать при непосредственном вызове методов:

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

Очевидно, что без эквивалента EndInvoke вы также не можете получить возвращаемое значение обратно, если вы не вызовете метод/поднять событие /etc в конце вашего метода... по соответствующей заметке, вам нужно чтобы быть осторожным с обработкой исключений.

Ответ 3

EndInvoke() имеет полезное, но редко упоминаемое поведение - он перескакивает все необработанные исключения, которые делегат генерирует в контексте исходного потока, поэтому вы можете переместить логику обработки исключений в основной код.

Кроме того, если у вашего делегата есть параметры /ref, они будут добавлены в подпись EndInvoke(), что позволит вам получить их, когда метод завершит выполнение.

Ответ 4

Если вы вызываете ThreadPool.QueueUserWorkItem, исключения, поднятые в рабочем элементе, будут необработаны в фоновом потоке (если вы явно не поймаете их). В .Net 2 и выше это прекратит ваш AppDomain.

Если вы вызываете делегата .BeginInvoke(), тогда исключения ставятся в очередь для повторного броска при вызове EndInvoke(). Если вы никогда не вызываете EndInvoke(), то исключения по существу являются "утечкой" памяти (как и любое другое состояние, не выпущенное асинхронной операцией).

Ответ 5

Не должно быть большой разницы, я также думаю, что сгенерированный BeginInvoke/EndInvoke для делегата использует пул потоков для выполнения.

Ответ 6

Не должно быть разницы в производительности, поскольку в потоке пула потоков выполняются как Delegate.BeginInvoke, так и ThreadPool.QueueUserWorkItem.

Самое большое различие заключается в том, что если вы вызываете BeginInvoke, вы должны в какой-то момент вызывать EndInvoke. Напротив, ThreadPool.QueueUserWorkItem - это "огонь и забыть". Это имеет преимущества и недостатки. Преимущество в том, что вы можете забыть об этом. Недостаток заключается в том, что у вас нет возможности узнать, если вы не добавите свой собственный механизм синхронизации/уведомления, когда задача завершится.