Правильное ли использование async/await

322
01 августа 2017, 14:45

Всем доброго времени суток.

Не так давно начал программировать на C#. По итогу написал следующее: DIPlaylist - программа создаёт DI.FM плейлист для AIMP с активированным премиумом на 7 дней.

Так как обучаюсь самостоятельно, то не могу оценить правильность кода. Впервые использовал async/await для того, чтобы основные задачи выполнялись вне главного потока, тем самым чтобы основное окно программы не подвисало. Но в текущем виде иногда замечаю, что это всё-таки происходит.

По итогу у меня возник вопрос, а правильно ли я использовал async/await? Так же буду премного благодарен за комментарии к остальному коду, для моего обучения это будет крайне полезно.

Код небольшой и в нём я старался комментировать все значимые и возможно непонятные на первый взгляд моменты.

Пример такого кода (вырезки):

private async void btnStart_Click(object sender, RoutedEventArgs e)
{
    progressBar.Value++;
    // Получение письма с премиум-ссылкой
    statusLabel.Content = await TempMail.GetLetter();
    if (String.IsNullOrEmpty(Settings.TmLetterID))
    {
        Status(false);
        return;
    }
    progressBar.Value++;
    // Создание и сохранение плейлиста
    statusLabel.Content = await Playlist.GoPlaylist();
    if (statusLabel.Content.ToString().ToLower().Contains("ошибка"))
    {
        Status(false);
        return;
    }
}

public static async Task<string> GetLetter()
{
    SetHeader();
    // 10 попыток с задержкой в 3 секунды на получение письма
    for (int i = 0; i < 10; i++)
    {
        await Task.Run(() =>
        {
            Thread.Sleep(3000);
        });
        string responseBody = "";
        try
        {
            HttpResponseMessage response = await httpClient.GetAsync(Settings.TmURL);
            responseBody = await response.Content.ReadAsStringAsync();
        }
        catch (Exception)
        {
            return "Проблема с доступом к " + Settings.TmURL;
        }
        Match match = new Regex("view\\/(.*?)\"", RegexOptions.IgnoreCase).Match(responseBody);
        if (match.Success)
        {
            Settings.TmLetterID = match.Groups[1].Value.Trim();
            return "Получение ключа на временный премиум...";
        }
    }
    return "Ошибка при получении письма с активацией";
}

public static async Task<string> GoPlaylist()
{
    try
    {
        List<string> channelsInfo = new List<string>();
        channelsInfo.Add($"#Name:Digitally Imported ({Settings.TmAddress}:{Settings.DiPass})");
        channelsInfo.Add("#Cursor:-1");
        JObject playlistJS = JObject.Parse(Settings.DiPlaylistJS);
        JToken[] channels = playlistJS["channels"].ToArray();
        channelsInfo.Add($"#Summary:{channels.Count().ToString()} / 00:00:00:00 / 0 B");
        channelsInfo.Add("#Flags:2047");
        channelsInfo.Add("#Group:Radio|1");
        int count = 0;
        return await Task.Run(() =>
        {
            foreach (var data in channels)
            {
                count++;
                channelsInfo.Add($"#Track:{count}|http://prem2.di.fm:80/{data["key"].ToString()}_hi?{Settings.DiListenKey}||||{data["name"].ToString()}|0|0|||0|0|0");
            }
            File.WriteAllLines($@"{Directory.GetCurrentDirectory()}\Digitally Imported.aimppl", channelsInfo);
            return "Плейлист успешно сгенерирован!";
        });
    }
    catch (Exception)
    {
        return "Ошибка при обработке плейлиста";
    }
}
Answer 1

важно понимать, что конструкции Task могут быть разными. Например, Task.Factory.StartNew использует асинхронные машины состояний, а конструкция Task.Run запускает отдельный поток на ЦП. Это очень хорошо описано в документации Майкрософт по платформе .Net.

await Task.Run(() =>
{
     Thread.Sleep(3000);
});

Здесь ожидание реализовано не очень хорошо, т.к. очевидно, что создавать новый поток только лишь для того, чтобы отложить выполнение метода - глупо, т.к. это весьма затратно. Используйте await Task.Delay(ms). Это что касается метода, в котором вы получаете данные по сети. Если рассмотреть другой пример, то может возникнуть несколько вопросов. Самый первый: почему у вас половина метода в таске, а половина - нет ? Оберните всё в таск и уберите async / await

try {
       return Task.Run(() => DoSomething());
   } 
   catch 
   {}
READ ALSO
C# кастомный ValidationAttribute не работает ASP.Net MVC

C# кастомный ValidationAttribute не работает ASP.Net MVC

Здравствуйте! Сделал свой атрибут валидации, который наследуется от ValidationAttribute, переопределил метод IsValid(object value, ValidationContext validationContext) и прописал...

362
Обработка клика по элементу в ListView

Обработка клика по элементу в ListView

Есть ListView, в котором отображаются папкиМне нужно, чтобы по клику на элементе, в другом ListView отображались список файлов в папке на которую...

301