async await: основной поток приостановлен?
Я читал о async/await
ключевые слова, и я прочитал это:
Когда поток логики достигает токена ожидания, вызывающий поток приостанавливается до завершения вызова.
Ну, я создал простой windows forms application
, поместил две метки, кнопку и текстовое поле, и я написал код:
private async void button1_Click(object sender, EventArgs e)
{
label1.Text = Thread.CurrentThread.ThreadState.ToString();
button1.Text = await DoWork();
label2.Text = Thread.CurrentThread.ThreadState.ToString();
}
private Task<string> DoWork()
{
return Task.Run(() => {
Thread.Sleep(10000);
return "done with work";
});
}
Что я не понимаю, так это то, что когда я нажимаю кнопку, на label1 будет отображаться текст Running
и метка будет иметь тот же текст только через 10 секунд, но за эти 10 секунд я смог ввести текст в текстовое поле, поэтому кажется, что основной поток работает...
Итак, как работает async/await?
С уважением
4 ответа
Я читал, что: когда поток логики достигает токена ожидания, вызывающий поток приостанавливается до завершения вызова.
Где ты прочитал эту ерунду? Либо там есть какой-то контекст, который вы не цитируете, либо вам следует прекратить читать любой текст, содержащий это. Смысл ожидания - сделать противоположное этому. Смысл ожидания состоит в том, чтобы текущий поток выполнял полезную работу во время выполнения асинхронной задачи.
ОБНОВЛЕНИЕ: Я загрузил книгу, на которую вы ссылались. Абсолютно все в этом разделе не так. Выбросьте эту книгу и купите лучшую книгу.
Что я не понимаю, так это то, что когда я нажимаю кнопку, на label1 будет отображаться текст Running, а на метке будет тот же текст только через 10 секунд, но за эти 10 секунд я смог ввести текст в своем текстовом поле, так что кажется, что основной поток запущен...
Правильно. Вот что происходит:
label1.Text = Thread.CurrentThread.ThreadState.ToString();
Текст установлен.
button1.Text = await DoWork();
Куча вещей происходит здесь. Что происходит первым? DoWork
называется. Что оно делает?
return Task.Run(() => { Thread.Sleep(10000);
Он извлекает поток из пула потоков, переводит его в спящий режим на десять секунд и возвращает задачу, представляющую "работу", выполняемую этим потоком.
Теперь мы вернулись сюда:
button1.Text = await DoWork();
У нас есть задача в руках. Await сначала проверяет задачу, чтобы убедиться, что она уже выполнена. Это не. Затем он подписывает оставшуюся часть этого метода как продолжение задачи. Затем он возвращается к своему абоненту.
Эй, каков его вызывающий? Как мы сюда попали?
Некоторый код назвал этот обработчик события; это был цикл событий, который обрабатывает сообщения Windows. Он увидел, что кнопка была нажата и отправлена в обработчик кликов, который только что вернулся.
Что теперь происходит? Цикл событий продолжает работать. Ваш пользовательский интерфейс продолжает работать хорошо, как вы заметили. В конце концов этот поток отсчитывает десять секунд, и продолжение задачи активируется. Что это делает?
Это отправляет сообщение в очередь Windows, в котором говорится: "Вам нужно запустить оставшуюся часть этого обработчика событий; у меня есть результат, который вы искали".
Цикл событий основного потока в конечном итоге попадает в это сообщение. Таким образом, обработчик события определяет, где он остановился:
button1.Text = await DoWork();
Теперь await извлекает результат из задачи, сохраняет его в тексте кнопки и возвращает обратно в цикл событий.
async
/await
создает конечные автоматы, которые обрабатывают продолжение для вас. Очень грубым эквивалентом (без множества функций) является явный метод продолжения, например:
private void button1_Click_Continuation(string value)
{
button1.Text = value;
label2.Text = Thread.CurrentThread.ThreadState.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = Thread.CurrentThread.ThreadState.ToString();
DoWork(button1_Click_Continuation);
}
private void DoWork(Action<string> continuation)
{
Task.Run(() => {
Thread.Sleep(10000);
continuation("done with work");
});
}
Обратите внимание, что я все еще использую Task.Run
бежать по отдельной нити. Обратите внимание, что в этой версии нет способа отслеживания прогресса (в то время как оригинал может изменить возвращаемое значение button1_Click
в Task
чтобы увидеть, завершено ли это или нет).
В дополнение к вышеупомянутому преобразованию система умна Task
запущен в потоке пользовательского интерфейса и снова выполняет маршалл обратно в поток пользовательского интерфейса, чтобы убедиться, что все работает, как вы ожидаете Это, вероятно, главное, чего вы не понимали, но расширение на аспекте конечного автомата иногда объясняет, что asyc
/await
действительно означает (вместо того, чтобы оставить это как мистическое).
Суть в том, что выполнение программы продолжается синхронно до тех пор, пока не встретится ожидание. как только ожидание встречается, предположим, что для задачи A компилятор переключится обратно на основной метод, который вызвал этот асинхронный метод без ключевого слова await , и продолжит выполнение программы с точки вызова задачи A, поскольку компилятор знает, что он должен ждать завершение задачи А, так почему бы не завершить другую отложенную задачу.
Здесь происходит следующее: событие button1_Click не ожидается в основном методе, поэтому оно будет продолжать выполняться, как только встретится await DoWork(). И после завершения DoWork() компилятор продолжит выполнять дальнейший код в button1_Click
Письменно await
, вы говорите - пожалуйста, остановите выполнение метода на этом этапе, дождитесь DoWork
закончить и только потом продолжить.
Асинхронное программирование с асинхронным и ожидающим (C#) разделом " Что происходит в разделе" Асинхронный метод "" содержит пошаговое объяснение с изображением того, что происходит в async
метод.
Еще лучшее объяснение в ожидании (C# ссылка). Посмотрите на комментарии для WaitSynchronously
а также WaitAsynchronouslyAsync
ниже.
В статье также говорится (выделено мое):
Оператор await применяется к задаче в асинхронном методе, чтобы приостановить выполнение метода до завершения ожидаемой задачи. Задача представляет текущую работу.
private async void button1_Click(object sender, EventArgs e)
{
// Call the method that runs asynchronously.
string result = await WaitAsynchronouslyAsync();
// Call the method that runs synchronously.
//string result = await WaitSynchronously ();
// Display the result.
textBox1.Text += result;
}
// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
await Task.Delay(10000);
return "Finished";
}
// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
// Add a using directive for System.Threading.
Thread.Sleep(10000);
return "Finished";
}