Как правильно реализовать async / await для переноса данных изображений? - PullRequest
0 голосов
/ 28 октября 2019

Я пишу консольное приложение для переноса данных между устаревшей системой и новой версией. С каждой записью связаны изображения, хранящиеся на одном веб-сервере, и я загружаю / изменяю / загружаю каждое изображение в Azure (а также записываю некоторые данные о каждом изображении в базу данных).

Вот примерный план в коде:

public void MigrateData()
{
    var records = GetRecords();

    foreach (var record in records)
    {
        // ...

        MigrateImages(record.Id, record.ImageCount);
    }
}

public void MigrateImages(int recordId, int imageCount)
{
    for (int i = 1; i <= imageCount; i++)
    {
        var legacyImageData = DownloadImage("the image url");

        if (legacyImageData != null && legacyImageData.Length > 0)
        {
            // discard because we don't need the image id here, but it's used in other workflows
            var _ = InsertImage(recordId, legacyImageData);
        }
    }
}

// This method can be used elsewhere, so the return of int is necessary and cannot be changed
public int InsertImage(int recordId, byte[] imageData)
{
    var urls = UploadImage(imageData).Result;

    return // method call to save image and return image ID
}

public async Task<(Uri LargeUri, Uri ThumbnailUri)> UploadImage(byte[] imageData)
{
    byte[] largeData = ResizeImageToLarge(imageData);
    byte[] thumbnailData = ResizeImageToThumbnail(imageData);

    var largeUpload = largeBlob.UploadFromByteArrayAsync(largeImage, 0, largeImage.Length);
    var thumbUpload = thumbsBlob.UploadFromByteArrayAsync(thumbImage, 0, thumbImage.Length);

    await Task.WhenAll(largeUpload, thumbUpload);

    var largeUrl = "";// logic to build url
    var thumbUrl = "";// logic to build url

    return (largeUrl, thumbUrl);
}

Я использую async / await для UploadImage(), чтобы разрешить параллельную загрузку больших и миниатюрных изображений, экономя время.

Мой вопрос (если это возможно / делаетсмысл) как я могу использовать async / await для MigrateImages() для параллельной загрузки изображений, чтобы сократить общее время, необходимое для выполнения задачи? Помогает ли моя цель тот факт, что я уже использую async / await в UploadImage()?

Это может быть очевидно из-за моего вопроса, но await / async - это все еще то, что я не могу полностью обдумать, как исправить, использовать и / или реализовать его.

1 Ответ

0 голосов
/ 29 октября 2019

Технология async / await предназначена для облегчения асинхронности, а не параллелизма. В вашем случае вы хотите ускорить процесс загрузки изображений, загружая несколько изображений одновременно. Это не важно, если вы тратите некоторые потоки пула потоков, блокируя их, и у вас нет потока пользовательского интерфейса, который должен оставаться разблокированным. Вы делаете инструмент, который собираетесь использовать один или два раза, и все. Поэтому я предлагаю вам избавиться от попыток понять, почему ваше приложение не ведет себя так, как вы ожидаете, и вообще избегать async / await. Придерживайтесь простой и знакомой модели синхронного программирования для этого задания.

Объединение синхронного и асинхронного кода опасно, и даже более того, если ваш опыт и понимание async / await ограничено. В этой технологии много хитростей. В частности, для свойства Task.Result используется красный флаг. Когда вы станете более опытным с кодом async / await, вы будете относиться к любому использованию Result как к незапертой гранате, готовой взорваться вам в лицо в любое время, и вы будете выглядеть как дура. При использовании в приложениях с контекстом синхронизации (Windows Forms, ASP.NET) он может так легко вводить взаимные блокировки, что это даже не смешно .

Вот как вы можете достичь желаемого параллелизма безнеобходимость разбираться со сложностями асинхронности:

public (Uri LargeUri, Uri ThumbnailUri) UploadImage(byte[] imageData)
{
    byte[] largeData = ResizeImageToLarge(imageData);
    byte[] thumbnailData = ResizeImageToThumbnail(imageData);

    var largeUpload = largeBlob.UploadFromByteArrayAsync(
        largeImage, 0, largeImage.Length);
    var thumbUpload = thumbsBlob.UploadFromByteArrayAsync(
        thumbImage, 0, thumbImage.Length);

    Task.WaitAll(largeUpload, thumbUpload);

    var largeUrl = "";// logic to build url
    var thumbUrl = "";// logic to build url

    return (largeUrl, thumbUrl);
}

Я просто заменил await Task.WhenAny на Task.WaitAll и удалил обертку Task из возвращаемого значения метода.

...