Почему async/await блокирует UI?

260
09 января 2018, 11:30

Пытаюсь разобраться с async/await.

Вызываю асинхронный метод:

public static async Task<Data> GetData(string url)
{
    dynamic results = await DataService.getData(url);
}

Cам метод:

public static async Task<dynamic> getData(string url)
{
    HttpClient client = new HttpClient();
    var response = await client.GetAsync(url);
    while(true){} // специально сделанное зацикливание, чтобы увидеть что UI не блокируется
    dynamic data = null;
    if(response !=null)
    {
        string json = response.Content.ReadAsStringAsync().Result;
        data = JsonConvert.DeserializeObject(json);
    }
    return data;
}

Однако при вызове этого метода всё зависает. Почему все зависает, ведь на сколько я понимаю, async\await должен создаваться в отдельном потоке и следовательно не должен блочить UI, а просто зацикливаться в "фоновом" потоке?

Answer 1

Нет, вы понимаете неправильно.

Если async-функция запущена в UI-потоке, она в нём и выполняется (это можно легко увидеть, залогировав Id потока), за исключением await’ов, во время которых функция вообще нигде не выполняется (и тем самым освобождает UI-поток). Иначе как бы можно было получать доступ к UI-контролам в async-функции? Уберите вечный цикл, он завешивает UI.

Что потенциально может вывести async-функцию в другой поток — это ConfigureAwait(false), но в вашей функции его нет.

У вас ошибка в строке

string json = response.Content.ReadAsStringAsync().Result;

Вы не должны синхронно дожидаться завершения Task’а ReadAsStringAsync(). Замените это на

string json = await response.Content.ReadAsStringAsync();

Кстати, у вас есть ещё одна потенциально медленная операция в UI-потоке: JsonConvert.DeserializeObject(json). Вынесите её в фоновый поток при помощи

data = await Task.Run(() => JsonConvert.DeserializeObject(json));

По поводу механики (как именно работает async-функция на нижнем уровне) можно почитать, например, тут, тут, тут, тут, тут и тут. И посмотреть тут.

READ ALSO
Получить HTML страницы в Gecko C#

Получить HTML страницы в Gecko C#

Если использовать обычный webBrowser, то можно вытащить HTML таким образом

247
Как анимировать свойство эффекта? - C# WPF

Как анимировать свойство эффекта? - C# WPF

Нужно плавно повысить у компонента background радиус блюра с 0 до 20, с помощью кода, то есть анимировать свойство radius в эффекте BlurEffect

311
Неправильное вращение

Неправильное вращение

Воспользовался QuaternionEuler() для вращения объектов

211
Как ускорить работу элемента System.Windows.Forms.ListView?

Как ускорить работу элемента System.Windows.Forms.ListView?

Столкнулся с проблемой в элементе управления SystemWindows

273