async / await работает синхронно? - PullRequest
0 голосов
/ 09 июля 2019

Ниже приведен код от https://blog.stephencleary.com/2012/02/async-and-await.html Я только что добавил несколько описательных методов

class Program
{
    async static Task Main(string[] args)
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId); //thread id is 1 here
        await DoSomethingAsync();
        Console.WriteLine("do other workzzz");
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);  //thread id is 4 here
        Console.ReadLine();
    }

    public static async Task DoSomethingAsync()
    {
        await Task.Delay(2000);    
    }
}

и автор говорит:

Мне нравится думать об «ожидании» как «асинхронном ожидании». То есть асинхронный метод приостанавливается до тех пор, пока ожидаемое не будет завершено (поэтому он ожидает), но реальный поток не блокируется (поэтому он асинхронный).

поэтому мои вопросы: почему фактический поток (идентификатор потока 1 в моем случае) не заблокирован? С моей точки зрения, поток заблокирован, потому что дальнейшие операторы не будут выполняться, пока текущий метод не завершится. Мы можем видеть, что Console.WriteLine("do other workzzz"); не будет выполняться до тех пор, пока DoSomethingAsync () не завершится, разве это не блокировка?

Еще одна важная вещь, на которую следует обратить внимание, - после завершения DoSomethingAsync(); идентификатор потока меняется с 1 на 4, «фактическая нить» больше не существует. Почему нить 1 исчезает? не должно ли быть нить 4 исчезнуть?

Ответы [ 4 ]

1 голос
/ 09 июля 2019

(Первоначальный вопрос звучал примерно так: «Что вы называете асинхронным методом, когда ожидаете его вместо того, чтобы запускать его параллельно?»)

Слово, которое я бы использовал, было «последовательно».Они работают по одному, в последовательности.Но задача такого типа (async) по-прежнему асинхронна из-за того, как она запланирована и выполнена.Это больше, чем просто вызывающий абонент, который решает, является ли он асинхронным или нет.(Кроме того, вызывающей стороной является async, а его вызывающей стороной также будет async, поэтому вы действительно не можете синхронно вызывать асинхронную задачу. (Если вы попытаетесь использовать такие функции, как Wait(), вы рискуете получить взаимоблокировку.капот, это не то же самое, что синхронная функция.)

0 голосов
/ 10 июля 2019

почему фактический поток (идентификатор потока 1 в моем случае) не заблокирован?

Когда await используется для ожидающего, который не завершен, то await возвращает из метода async. Вызывающий поток продолжает выполнять свой следующий фрагмент кода.

Мы можем видеть, что Console.WriteLine («делай другую работу»); не будет выполняться до тех пор, пока DoSomethingAsync () не завершится, разве это не блокировка?

Не блокирует поток. Метод «приостановлен» в точке await, но нет никакого потока, удерживающего его на месте.

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

Еще одна важная вещь, которую стоит отметить, после DoSomethingAsync (); завершается, идентификатор потока изменяется с 1 на 4, «фактического потока» больше нет. Почему нить 1 исчезает? не должно ли быть нить 4 исчезнуть?

Обе темы 1 и 4 являются фактическими. Поток 1 - это основной поток, который запускает консольное приложение. Поток 4 является потоком пула потоков.

Когда DoSomethingAsync заканчивается и await готов возобновить свой метод async, он должен возобновить его где-то . По умолчанию await захватывает «контекст»; для консольного приложения это контекст пула потоков. Поэтому, когда метод async возобновляется, он возобновляется в потоке пула потоков.

0 голосов
/ 10 июля 2019

Асинхронные операции обрабатываются иначе, чем другие методы.

Упрощенное объяснение: ваш код ..

async static Task Main(string[] args) 
{
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId); //thread id is 1 here
    await DoSomethingAsync();
    Console.WriteLine("do other workzzz");
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);  //thread id is 4 here
    Console.ReadLine(); 
}

Выполняется следующим образом:

Вызывающий поток выполняет ..

Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

Код, ожидаемый оператором await, отправляется планировщику задач, который использует контекст синхронизации для постановки в очередь, планирования и выполнения. Реализация контекста синхронизации отвечает за решение, в каком потоке этот код выполняется.

 await DoSomethingAsync();

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

Console.WriteLine("do other workzzz");
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
Console.ReadLine(); 

Несмотря на то, что код читается синхронно, шаблон async/await прервет поток.

0 голосов
/ 09 июля 2019

Я думаю, что отсутствие переменных назначений в части B сбивает вас с толку.

Часть B можно переписать так:

var a = Task.Delay(1000);
await a;

var b = Task.Delay(1000);
await b;

var c = Task.Delay(1000);
await c;

etc...

Если вы вызываете все задачи в начале и ожидаете их всех в конце (как в части А), то вы можете думать об этом как о тех задачах, которые выполняются параллельно.

await означает ожидание завершения задания (в данном случае Task.Delay(1000)), прежде чем двигаться дальше. Без await вы можете думать о задаче как о выполняющейся в фоновом режиме (но основная программа может выйти до того, как она достигнет завершения)

Чтобы дождаться завершения всех задач до выхода из программы, вы можете сделать что-то вроде этого: await Task.WhenAll(taskA, taskB, …);

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