Почему блокируется WebClient.DownloadStringTaskAsync ()?- новый асинхронный API / синтаксис / CTP - PullRequest
15 голосов
/ 30 октября 2010

По какой-то причине после запуска указанной ниже программы возникает пауза. Я считаю, что WebClient().DownloadStringTaskAsync() является причиной.

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

        for (int i = 0; i < 15; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }

    public static async void AsyncReturnTask()
    {
        var result = await DownloadAndReturnTaskStringAsync();
        Console.WriteLine(result);
    }

    private static async Task<string> DownloadAndReturnTaskStringAsync()
    {
        return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov"));
    }
}

Насколько я понимаю, моя программа должна немедленно начать отсчет от 0 до 15. Я что-то не так делаю?

У меня была такая же проблема с исходным образцом загрузки Netflix (который вы получаете с CTP ) - после нажатия кнопки поиска пользовательский интерфейс сначала зависает - и через некоторое время он реагирует при загрузке следующих фильмов , И я считаю, что это не замерзло в презентации Андерса Хейлсберга на PDC 2010.

Еще одна вещь. Когда вместо

return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov"));

Я использую свой собственный метод:

return await ReturnOrdinaryTask();

Что такое:

public static Task<string> ReturnOrdinaryTask()
{
    var t = Task.Factory.StartNew(() =>
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("------------- " + i.ToString());
            Thread.Sleep(100);
        }
        return "some text";
    });
    return t;
}

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

Редактировать

Хорошо, сейчас я верю в то, что функция WebClient.DownloadStringTaskAsync испорчена. Он должен работать без начального периода блокировки, например:

    static void Main(string[] args)
    {
        WebClient cli = new WebClient();
        Task.Factory.StartNew(() =>
            {
                cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result);
                cli.DownloadStringAsync(new Uri("http://www.weather.gov"));
            });

        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }

Ответы [ 3 ]

15 голосов
/ 30 октября 2010

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

Помните, что новый асинхронный API все еще однопоточный. Таким образом, WebClient().DownloadStringTaskAsync() по-прежнему должен выполняться в вашем потоке до тех пор, пока запрос не будет подготовлен и отправлен на сервер, прежде чем он сможет await и вернуть выполнение в поток вашей программы в Main ().

Я думаю, что результаты, которые вы видите, связаны с тем, что создание и отправка запроса с вашего компьютера занимает некоторое время. Сначала, когда это закончится, реализация DownloadStringTaskAsync может ожидать завершения сетевого ввода-вывода и удаленного сервера и может вернуть вам выполнение.

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

Вот несколько ссылок на эту тему: Блог Эрика Липперта (один из разработчиков языков), а также Первоначальное сообщение в блоге Джона Скита об этом. У Эрика есть серия из 5 постов о стиле прохождения продолжения, о чем на самом деле async и await. Если вы хотите понять новую функцию в деталях, вы можете прочитать сообщения Эрика о CPS и Async. В любом случае, обе ссылки выше хорошо объясняют очень важный факт:

  • Асинхронный! = Параллельный

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

Редактировать

Просто чтобы понять, что происходит: DownloadStringTaskAsync устанавливает продолжение, затем вызывает WebClient.DownloadStringAsync в том же потоке, а , а затем возвращает выполнение к вашему коду. Следовательно, время блокировки, которое вы видите перед началом подсчета цикла, - это время, которое требуется DownloadStringAsync для завершения. Ваша программа с async и await очень похожа на следующую программу, которая демонстрирует то же поведение, что и ваша программа: начальный блок, затем начинается отсчет, и где-то посередине асинхронная операция завершается и печатает содержимое из запрошенный URL:

    static void Main(string[] args)
    {
        WebClient cli = new WebClient();
        cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result);
        cli.DownloadStringAsync(new Uri("http://www.weather.gov")); // Blocks until request has been prepared

        for (int i = 0; i < 15; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }

Примечание: я ни в коем случае не эксперт в этом вопросе, поэтому я могу ошибаться в некоторых моментах. Не стесняйтесь исправить мое понимание предмета, если вы считаете, что это неправильно - я только что посмотрел презентацию PDC и поиграл с CTP вчера вечером.

5 голосов
/ 02 февраля 2011

Вы уверены, что проблема не связана с параметрами конфигурации прокси, обнаруживаемыми из IE / Registry / Somewhere Slow?

Попробуйте установить webClient.Proxy = null (или указать параметры в app.config) иВаш период "блокировки" должен быть минимальным.

2 голосов
/ 30 октября 2010

Вы нажимаете F5 или CTLR + F5, чтобы запустить его?С F5 есть задержка для VS только для поиска символов AsyncCtpLibrary.dll ...

...