async await: приостановлен ли основной поток?

Я читал о async/await ключевые слова и я читал, что:

когда поток логики достигает токена 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?

вот "скриншот" из книги: enter image description here

в отношении

3 ответов


Я прочитал это: когда поток логики достигает токена await, вызывающий поток приостанавливается до завершения вызова.

где вы читали эту чушь? Либо там есть какой-то контекст, который вы не цитируете, либо вы должны прекратить читать любой текст, который содержит это. Смысл ожидания состоит в том, чтобы сделать напротив об этом. Точка ожидания чтобы текущий поток выполнял полезную работу, пока асинхронная задача в полете.

UPDATE: я загрузил книгу, на которую вы ссылались. Абсолютно все в этом разделе неправильно. Выбросьте эту книгу и купите книгу получше.

чего я не понимаю, так это того, что когда я нажимаю кнопку, label1 будет иметь текст, и метка будет иметь тот же текст только через 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. Он увидел, что кнопка была нажата и отправлена обработчику click, который только что возвращенный.

что происходит? Цикл событий продолжает работать. Ваш UI продолжает работать хорошо, как вы заметили. В конце концов этот поток отсчитывает десять секунд, и продолжение задачи активируется. Что это значит?

это отправляет сообщение в очередь Windows, говоря: "вам нужно запустить остальную часть этого обработчика событий сейчас; у меня есть результат, который вы искали."

цикл событий основного потока в конечном итоге попадает в это сообщение. Поэтому обработчик событий выбирает вверх, где он остановился:

        button1.Text =  await DoWork();

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


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 действительно означает (вместо того, чтобы оставить его как мистический).


писать await, вы говорите - пожалуйста, остановите выполнение метода на этом этапе, подождите DoWork закончить и только затем продолжить.

асинхронное программирование с async и await (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";
}