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";
}
Другие вопросы по тегам