В чем разница между Invoke () и BeginInvoke ()

398

Просто интересно, какая разница между BeginInvoke()и Invoke()есть?

Главным образом, для чего будет использоваться каждый.

РЕДАКТИРОВАТЬ: В чем разница между созданием потокового объекта и вызова invoke на этом и просто вызов BeginInvoke()делегата? или это одно и то же?

Натан У
источник

Ответы:

569

Вы имеете в виду 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, обычно в обратном вызове.

Джон Скит
источник
4
Тогда зачем бы людям использовать Invoke вместо BeingInvoke? Не должно быть каких-то преимуществ перед использованием Invoke. Оба выполняют процессы в фоновом режиме, только один находится в одном потоке, другой - в другом?
10
2
@Jon: пока я использую Dispatcher.BeginInvoke, мой код работает нормально и в Dispatcher.Invoke мое приложение заставляет меня ждать несколько секунд, затем оно инициализирует все элементы управления и запускается. Не могли бы вы помочь мне выяснить, в каком именно месте я застрял ?
SharpUrBrain
6
@SharpUrBrain: Control.BeginInvoke является своего рода эквивалентом Dispatcher.BeginInvoke, но для WinForms (тогда как Dispatcher для WPF и Silverlight).
Джон Скит
4
@SharpUrBrain: я бы посоветовал вам задать конкретный вопрос, а не продолжать в комментариях - и, конечно же, проверить, был ли этот же вопрос уже задан кем-то другим в первую очередь.
Джон Скит
2
@AZ: Да, под «в потоке пользовательского интерфейса» он подразумевает конкретный «поток пользовательского интерфейса», который владеет дескриптором этого конкретного элемента управления. Как правило, существует только один поток пользовательского интерфейса, но возможно иметь несколько потоков пользовательского интерфейса, и в сложных приложениях есть причины, по которым вы хотели бы их получить. Технически, любой (нормальный?) Поток может запустить насос сообщений пользовательского интерфейса и стать потоком пользовательского интерфейса - и может позже отключить насос сообщений и больше не быть потоком пользовательского интерфейса. (Я предполагаю, что это не то, что можно попробовать в потоке потоков),
Роб Паркер,
46

Основываясь на ответе Джона Скита, бывают случаи, когда вы хотите вызвать делегата и дождаться его выполнения до того, как текущий поток продолжится. В этих случаях вызов Invoke - это то, что вы хотите.

В многопоточных приложениях вы можете не захотеть, чтобы поток ожидал делегата, чтобы завершить выполнение, особенно если этот делегат выполняет ввод / вывод (что может сделать делегат и ваш поток заблокированным).

В этих случаях BeginInvoke будет полезен. Вызывая его, вы приказываете делегату начать, но тогда ваш поток может делать другие вещи параллельно с делегатом.

Использование BeginInvoke увеличивает сложность вашего кода, но бывают случаи, когда повышенная производительность оправдывает себя.

Тим Стюарт
источник
27

Разница между Control.Invoke()и Control.BeginInvoke()есть,

  • BeginInvoke()запланирует асинхронное действие в потоке GUI. Когда запланировано асинхронное действие, ваш код продолжается. Через некоторое время (вы точно не знаете, когда) ваше асинхронное действие будет выполнено
  • Invoke() выполнит ваше асинхронное действие (в потоке GUI) и будет ждать, пока ваше действие не будет завершено.

Логическим выводом является то, что делегат, которому вы передаете, Invoke()может иметь out-параметры или возвращаемое значение, в то время как делегат, BeginInvoke()которому вы передаете, не может (вы должны использовать EndInvoke для получения результатов).

Sujit
источник
20

Просто чтобы дать короткий, рабочий пример, чтобы увидеть эффект от их различия

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 ) вызовов.

КМК
источник
9

Delegate.BeginInvoke () асинхронно ставит в очередь вызов делегата и немедленно возвращает управление. При использовании Delegate.BeginInvoke () вы должны вызвать Delegate.EndInvoke () в методе обратного вызова, чтобы получить результаты.

Delegate.Invoke () синхронно вызывает делегата в том же потоке.

Статья MSDN

Аарон Палмер
источник
8

Просто добавив, почему и когда использовать Invoke ().

И Invoke (), и BeginInvoke () направляют указанный код в поток диспетчера.

Но в отличие от BeginInvoke (), Invoke () останавливает ваш поток до тех пор, пока диспетчер не выполнит ваш код. Возможно, вы захотите использовать Invoke (), если вам нужно приостановить асинхронную операцию, пока пользователь не предоставит какую-то обратную связь.

Например, вы можете вызвать Invoke () для запуска фрагмента кода, который показывает диалоговое окно OK / Cancel. После того, как пользователь нажмет кнопку и ваш маршалинг-код завершится, метод invoke () вернется, и вы сможете действовать в соответствии с ответом пользователя.

Смотрите Pro WPF в C # главе 31

Ingako
источник