После моего вопроса BeginInvoke()/EndInvoke() существуют ли существенные различия в производительности/что-то еще между Delegate.BeginInvoke() и использованием QueueUserWorkItem() для асинхронного вызова делегата?
Какая разница между QueueUserWorkItem() и BeginInvoke(), для выполнения асинхронной активности без необходимости возврата типов
Ответ 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 - это "огонь и забыть". Это имеет преимущества и недостатки. Преимущество в том, что вы можете забыть об этом. Недостаток заключается в том, что у вас нет возможности узнать, если вы не добавите свой собственный механизм синхронизации/уведомления, когда задача завершится.