Почему в вызове компонента Cross Thread UI нет EndInvoke?
Я пытался добавить строку в какой-то ListBox, который у меня есть в приложении (простая winform) - и я сделал это с помощью BeginInboke
myListBox.BeginInvoke(new Action(delegate()
{
myListBox.Items.Add( "some string" ));
}));
После того, как я снова прочитал эти 3 строки - и я не понимаю, почему в каком-либо примере интерфейса с несколькими нитями, который я смотрю в Google и MSDN, я не вижу никакого вызова EndInvoke? Есть ли причина не вызывать EndInvoke в этом случае?
2 ответа
Это был неудачный выбор имен в.NET. Методы Control.BeginInvoke и Dispatcher.BeginInvoke имеют то же имя, что и методы делегата, но работают совершенно по- разному. Основные отличия:
Метод BeginInvoke() делегата всегда безопасен от типа и имеет те же аргументы, что и объявление делегата. Это полностью отсутствует в версиях Control/Dispatcher, аргументы передаются через массив params типа object[]. Компилятор не скажет вам, если вы ошиблись аргументом, он бомбит во время выполнения
Метод Invoke () делегата запускает цель делегата в том же потоке. Не относится к Control/Dispatcher.Invoke(), они перенаправляют вызов в поток пользовательского интерфейса
Исключение, которое выдается в цели BeginInvoke() делегата, перехватывается и не приводит к сбою программы. Быть переброшенным при вызове EndInvoke(). Это совсем не так для Control/Dispatcher.BeginInvoke(), они вызывают исключение в потоке пользовательского интерфейса. Без приличного способа отловить исключение - одна из причин, по которой существует Application.UnhandledException.
Вызов метода EndInvoke () делегата является обязательным, в противном случае он вызывает утечку ресурсов в течение 10 минут. Это не требуется для методов Control/Dispatcher.BeginInvoke(), и вы никогда не делаете это на практике.
Использование Control/Dispatcher.Invoke () сопряжено с риском, что может привести к взаимоблокировке. Срабатывает, когда поток пользовательского интерфейса не готов вызвать цель и делает что-то неразумное, например, ожидание завершения потока. Не проблема для делегата, не в последнюю очередь потому, что его метод Invoke () не использует поток.
Вызов Control/Dispatcher.BeginInvoke() в потоке пользовательского интерфейса является поддерживаемым сценарием. Как и ожидалось, цель все еще работает в потоке пользовательского интерфейса. Но позже, после того, как поток пользовательского интерфейса снова простаивает и снова входит в цикл диспетчера. На самом деле это очень полезная функция, она помогает решить сложные проблемы с повторным входом. Особенно в обработчиках событий для элементов управления пользовательского интерфейса, которые будут плохо себя вести, когда вы запускаете код со слишком большим количеством побочных эффектов.
Большой список с подробностями реализации. Версия TLDR, безусловно, такова: "Они не имеют ничего общего, не вызывая EndInvoke - это нормально и совершенно нормально".
Control.BeginInvoke
не похоже полностью следовать обычному BeginX
/ EndX
шаблон ака модель асинхронного программирования (APM). Обычно вы должны позвонить EndX
для каждого BeginX
, но в случае Control.BeginInvoke
это не обязательно:
"Вы можете позвонить
EndInvoke
чтобы получить возвращаемое значение от делегата, если это необходимо, но это не обязательно. EndInvoke будет блокироваться, пока не будет получено возвращаемое значение."- из раздела "Примечания" на справочной странице MSDN для
Control.BeginInvoke
(акцент мной)
И на практике это вряд ли когда-либо необходимо. Это связано с тем, что метод обычно вызывается для выполнения некоторого кода в потоке пользовательского интерфейса, который обновляет пользовательский интерфейс. Обновление пользовательского интерфейса обычно не дает никакого возвращаемого значения, поэтому вы не захотите вызывать EndInvoke
,