asyn c / await, как правильно использовать - PullRequest
0 голосов
/ 07 мая 2020

Я пытаюсь изучить c# async / await, все уроки, которые я нашел, одинаковы, и я ничего не могу понять, например, почему этот код не работает? Не могли бы вы объяснить, как и почему async / await, или дайте несколько полезных ссылок, чтобы по-настоящему понять, учитывая, что у меня нет опыта с этим.

class Program
    {
        static void Main(string[] args)
        {
            Waiter();
        }

        public static async void Waiter()
        {
            await HeavyStuff();
            Console.WriteLine("1");
        }

        public static async Task HeavyStuff()
        {
            await Task.Run(() => {
                Thread.Sleep(1000);
                Console.WriteLine("2");
            });
        }
    }

Ответы [ 2 ]

3 голосов
/ 07 мая 2020

Я бы посоветовал вам прочитать сообщение в блоге Стивена Клири для довольно исчерпывающего объяснения того, что такое async / await.

Однако конкретно на ваш вопрос, использование of async в сигнатуре метода предлагает компилятору создать конечный автомат. Вы должны поместить ключевое слово async в метод, чтобы разрешить использование await в теле метода (как вы, вероятно, обнаружили при написании Waiter() и HeavyStuff()).

Что в конечном итоге произойдет вот когда вы вызываете Waiter из Main, он действительно вызывает метод Waiter, но поскольку вы не ожидаете результата вызова, он обрабатывает его как «выстрелил и забыл». Таким образом, вы испытываете закрытие программы.

Аналогичным образом, пока Waiter() ожидает HeavyStuff() (если Waiter () ожидали от Main()), программа будет ждать возврата этого метода, прежде чем отобразить «1» на консоли.

Сообщение в блоге выше описывает это, но вы, вероятно, не захотите возвращать void из Waiter(), потому что это обычно используется для обработки событий, когда на самом деле ничего не ожидает результата метод. Пока вы не возвращаете значение с помощью своего метода, вы его ожидаете, поэтому лучше вместо этого возвращать Task.

Начиная с C# 7.1, вам разрешено использовать ключевое слово async в вашей точке входа Main(), поэтому ваш код должен быть следующим:

class Program
{
    static async Task Main(string[] args)
    {
        await Waiter();
    }

    public static async Task Waiter()
    {
        await HeavyStuff();
        Console.WriteLine("1");
    }

    public static async Task HeavyStuff()
    {
        await Task.Delay(1000);
        Console.WriteLine("2");
    }
}
3 голосов
/ 07 мая 2020

await the Waiter () в Main (). В противном случае он вызовет Waiter (), но Main () включит go, не дожидаясь, пока Waiter () завершит sh. Это называется «выстрелил и забыл». Main () завершится sh, и программа завершится до завершения Waiter ().

class Program
{
    static async Task Main(string[] args)
    {
        await Waiter();
    }

    public static async Task Waiter()
    {
        Console.WriteLine("1");
        await HeavyStuff();
        Console.WriteLine("4"); // this will be instantly shown after "3"
    }

    public static async Task HeavyStuff()
    {
        Console.WriteLine("2");  // this will be instantly shown after "1"
        await Task.Delay(1000);  // short version of Task.Run(() => Thread.Sleep(1000))
        Console.WriteLine("3");  // this will be shown after 1 second
    }
}

Версия с забытым огнем без asyn c Main. В этой версии мы предотвращаем завершение основного (= завершение программы) до того, как официант завершит sh.

class Program
{
    static void Main(string[] args)
    {
        Waiter();
        Thread.Sleep(2000);  // this gives Waiter() time to finish
    }

    public static async Task Waiter()
    {
        Console.WriteLine("1");
        await HeavyStuff();
        Console.WriteLine("4");
    }

    public static async Task HeavyStuff()
    {
        Console.WriteLine("2");
        await Task.Delay(1000);
        Console.WriteLine("3");
    }
}
...