Ответы:
Вы имеете в виду Delegate.Invoke
/ BeginInvoke
или Control.Invoke
/ BeginInvoke
?
Delegate.Invoke
: Выполняется синхронно в том же потоке.Delegate.BeginInvoke
: Выполняется асинхронно в threadpool
потоке.Control.Invoke
: Выполняется в потоке пользовательского интерфейса, но вызывающий поток ожидает завершения, прежде чем продолжить.Control.BeginInvoke
: Выполняется в потоке пользовательского интерфейса, и вызывающий поток не ожидает завершения.В ответе Тима упоминается, когда вы, возможно, захотите использовать BeginInvoke
- хотя Delegate.BeginInvoke
я подозреваю, что он был в основном ориентирован на .
Для приложений Windows Forms я бы предложил, чтобы вы обычно использовали BeginInvoke
. Таким образом, вам не нужно беспокоиться о взаимоблокировке, например, - но вы должны понимать, что пользовательский интерфейс, возможно, не был обновлен, когда вы в следующий раз посмотрите на него! В частности, вы не должны изменять данные, которые поток пользовательского интерфейса может использовать для отображения. Например, если у вас есть Person
с FirstName
и LastName
свойствами, и вы сделали:
person.FirstName = "Kevin"; // person is a shared reference
person.LastName = "Spacey";
control.BeginInvoke(UpdateName);
person.FirstName = "Keyser";
person.LastName = "Soze";
Тогда пользовательский интерфейс может в конечном итоге отображать «Keyser Spacey». (Существует внешний шанс, что он может отобразить «Кевин Созе», но только через странность модели памяти.)
Однако, если у вас нет такого рода проблем, их Control.BeginInvoke
будет проще понять, и вы избежите от фонового потока необходимости ждать без уважительной причины. Обратите внимание, что команда Windows Forms гарантировала, что вы можете использовать метод Control.BeginInvoke
«запусти и забудь», то есть не звоня EndInvoke
. Это не относится к асинхронным вызовам в целом: обычно каждый BeginXXX должен иметь соответствующий вызов EndXXX, обычно в обратном вызове.
Основываясь на ответе Джона Скита, бывают случаи, когда вы хотите вызвать делегата и дождаться его выполнения до того, как текущий поток продолжится. В этих случаях вызов Invoke - это то, что вы хотите.
В многопоточных приложениях вы можете не захотеть, чтобы поток ожидал делегата, чтобы завершить выполнение, особенно если этот делегат выполняет ввод / вывод (что может сделать делегат и ваш поток заблокированным).
В этих случаях BeginInvoke будет полезен. Вызывая его, вы приказываете делегату начать, но тогда ваш поток может делать другие вещи параллельно с делегатом.
Использование BeginInvoke увеличивает сложность вашего кода, но бывают случаи, когда повышенная производительность оправдывает себя.
Разница между Control.Invoke()
и Control.BeginInvoke()
есть,
BeginInvoke()
запланирует асинхронное действие в потоке GUI. Когда запланировано асинхронное действие, ваш код продолжается. Через некоторое время (вы точно не знаете, когда) ваше асинхронное действие будет выполненоInvoke()
выполнит ваше асинхронное действие (в потоке GUI) и будет ждать, пока ваше действие не будет завершено.Логическим выводом является то, что делегат, которому вы передаете, Invoke()
может иметь out-параметры или возвращаемое значение, в то время как делегат, BeginInvoke()
которому вы передаете, не может (вы должны использовать EndInvoke для получения результатов).
Просто чтобы дать короткий, рабочий пример, чтобы увидеть эффект от их различия
new Thread(foo).Start();
private void foo()
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate()
{
myTextBox.Text = "bing";
Thread.Sleep(TimeSpan.FromSeconds(3));
});
MessageBox.Show("done");
}
Если используется BeginInvoke , MessageBox выскакивает одновременно с обновлением текста. Если использовать Invoke , MessageBox появляется после 3 секунд сна. Отсюда показано влияние асинхронного ( BeginInvoke ) и синхронного ( Invoke ) вызовов.
Delegate.BeginInvoke () асинхронно ставит в очередь вызов делегата и немедленно возвращает управление. При использовании Delegate.BeginInvoke () вы должны вызвать Delegate.EndInvoke () в методе обратного вызова, чтобы получить результаты.
Delegate.Invoke () синхронно вызывает делегата в том же потоке.
Просто добавив, почему и когда использовать Invoke ().
И Invoke (), и BeginInvoke () направляют указанный код в поток диспетчера.
Но в отличие от BeginInvoke (), Invoke () останавливает ваш поток до тех пор, пока диспетчер не выполнит ваш код. Возможно, вы захотите использовать Invoke (), если вам нужно приостановить асинхронную операцию, пока пользователь не предоставит какую-то обратную связь.
Например, вы можете вызвать Invoke () для запуска фрагмента кода, который показывает диалоговое окно OK / Cancel. После того, как пользователь нажмет кнопку и ваш маршалинг-код завершится, метод invoke () вернется, и вы сможете действовать в соответствии с ответом пользователя.
Смотрите Pro WPF в C # главе 31