Асинхронное программирование await async

Рейтинг: 2Ответов: 3Опубликовано: 23.03.2015

alt textВсем привет!

Пытаюсь разобраться с асинхронным вызовом методов. Узнал такую вещь: если предполагается, что некоторый метод может долго выполнятся, то его можно перенести в "фоновое" выполнение, а управление отдать в вызывающий метод. Решил разобраться с этим и написал для начала обычный код без асинхронного программирования.

class Test
{
    public  static void Download()
    {        
        WebClient wc = new WebClient();
        byte[] data = wc.DownloadData(@"http://www.ukraine.com.ua/static/100MB.bin");
        Console.WriteLine("Welcome to");
        Console.WriteLine("Hell");
        Console.WriteLine(data.Length);
    }

    static void Main()
    {
        Download();
    }
}

Здесь вроде все понятно, пока загружается 100мб файл, соответственно остальные инструкции в методе Download() не выполняются до окончания загрузки. То есть идет последовательное выполнение. Далее я решил поэкспериментировать с await и async и написал следующий код:

class Test
{
    public async  static void Download()
    {        
        WebClient wc = new WebClient();
        byte[] data = await wc.DownloadDataTaskAsync(@"http://www.ukraine.com.ua/static/100MB.bin");        
        Console.WriteLine("Welcome to");
        Console.WriteLine("Hell");
        Console.WriteLine(data.Length);
    }

    static void Main()
    {
        Download();
    }
}

Как видно, логика кода такая же, только добавлены await и async и под них изменен метод на DownloadDataTaskAsync.

В этом коде я ожидал такое поведение:

  1. Файл начинает скачиваться.
  2. Так как закачка переходит в фоновый режим, то управление возвращается методу Download(), и следовательно выводится на консоль Welcome to Hell.
  3. По окончании загрузки файла выводится результат Lengеth.

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

Спасибо.

Ответы

▲ 2Принят

Управление методу Download тоже передается, просто вы его сразу же возвращаете. Если хочется параллельности - надо разнести по времени запуск задачи и ее ожидание. Надо понимать, что асинхронные методы всего лишь возвращают объект Task (или Task<>) - который всегда можно сохранить в переменную.

Вот вам такой пример:

class Test
{
    public static async Task Download()
    {        
        WebClient wc = new WebClient();
        var task = wc.DownloadData(@"http://www.ukraine.com.ua/static/100MB.bin");
        Console.WriteLine("Welcome to");
        Console.WriteLine("Hell");
        Console.WriteLine((await task).Length);
    }

    static void Main()
    {
        Download().Wait();
    }
}

Это то, чего вы пытались добиться?

▲ 2

Как-то так оно должно быть.

internal class Program {
        public static async Task<byte[]> Download() {
            WebClient wc = new WebClient();
            byte[] data = await wc.DownloadDataTaskAsync(@"https://www.google.de/logos/doodles/2015/emmy-noethers-133rd-birthday-5681045017985024-hp.jpg");
            return data;
        }

        private static async void ProcessDataAsync() {
            Console.WriteLine("Begin download...");
            var task = Download();
            var result = await task;
            Console.WriteLine("Bytes downloaded: " + result.Length);
        }

        private static void Main(string[] args) {
            Task task = new Task(ProcessDataAsync);
            task.Start();
            Console.WriteLine("Welcome to");
            Console.WriteLine("Hell");

            task.Wait();
            Console.ReadLine();
        }
    }

Или можно вот так:

internal class Program {
    public static async Task<byte[]> Download() {
        WebClient wc = new WebClient();
        byte[] data = await wc.DownloadDataTaskAsync(@"https://www.google.de/logos/doodles/2015/emmy-noethers-133rd-birthday-5681045017985024-hp.jpg");
        return data;
    }

    private static void Main(string[] args) {
        var task = Download();
        Console.WriteLine("Welcome to");
        Console.WriteLine("Hell");

        Task.WaitAll(task);
        Console.WriteLine("Bytes downloaded: " + task.Result.Length);
        Console.ReadLine();
    }
}
▲ 1

Разобрался! Моя ошибка была в том, что я предполагал, будто управление при вызове метода DownloadDataTaskAsync передается методу Download (в чем меня сбил скриншот из книги). После нехитрых манипуляций с кодом, понял, что управление передается методу, который вызывает Download, т.е. методу Main().

class Test
{
    public async static void Download()
    {
        WebClient wc = new WebClient();
        byte[] data = await wc.DownloadDataTaskAsync(@"http://www.ukraine.com.ua/static/100MB.bin");
        Console.WriteLine("Welcome to");
        Console.WriteLine("Hell");
        Console.WriteLine(data.Length);
    }

    static void Main()
    {
        Download();
        Thread.Sleep(100000);
    }
}