Відповіді:
Ви маєте на увазі 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()
може мати параметри або значення, що повертаються, а делегат, якому ви 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 / Скасувати. Після того, як користувач натисне на кнопку і ваш маршований код завершиться, метод invoke () повернеться, і ви можете діяти за відповіддю користувача.
Дивіться Pro WPF в C # главі 31