async await веб-файл api io

Я использую следующий асинхронный код в контроллере веб-API для обработки XML-файла. Все работает, как ожидалось, однако это правильное использование подхода async / await. Я в основном извлекаю все изображения из файла XML и затем сохраняю их на диск. Я хотел попытаться минимизировать влияние файла io.

public async Task<HttpResponseMessage> PostFile()
{
    await Task.WhenAll(this.ProcessProofOfPressenceImages(address, images, surveyReference), this.ProcessSketchImages(propertyPlans, images, surveyReference), this.ProcessExteriorImages(exteriorSketch, images, surveyReference));
    //db code
}

private async Task ProcessProofOfPressenceImages(Dictionary<object, object> container, List<Dictionary<string, string>> images, string surveyReference)
{
    if(images != null)
    {
        await Task.WhenAll(this.ProcessImagesHelper(container, images, surveyReference, "propertyImage"));
    }
}

private async Task ProcessSketchImages(Dictionary<object, object> container, List<Dictionary<string, string>> images, string surveyReference)
{
    if(images != null)
    {
        await Task.WhenAll(this.ProcessImagesHelper(container, images, surveyReference, "sketchPlanImage"), this.ProcessImagesHelper(container, images, surveyReference, "sketchFrontImage"), this.ProcessImagesHelper(container, images, surveyReference, "sketchRearImage"), this.ProcessImagesHelper(container, images, surveyReference, "sketchLeftSideImage"), this.ProcessImagesHelper(container, images, surveyReference, "sketchRightSideImage"));
    }
}

private async Task ProcessExteriorImages(Dictionary<object, object> container, List<Dictionary<string, string>> images, string surveyReference)
{
    List<Task> tasks = new List<Task>();

    if(images != null)
    {
        await Task.WhenAll(this.ProcessImagesHelper(container, images, surveyReference, "image1"), this.ProcessImagesHelper(container, images, surveyReference, "image2"), this.ProcessImagesHelper(container, images, surveyReference, "image3"), this.ProcessImagesHelper(container, images, surveyReference, "image4"), this.ProcessImagesHelper(container, images, surveyReference, "image5"), this.ProcessImagesHelper(container, images, surveyReference, "image6"));
    }
}

private async Task ProcessImagesHelper(Dictionary<object, object> container, List<Dictionary<string, string>> images, string surveyReference, string image)
{
    if(container.ContainsKey(image) && !String.IsNullOrEmpty(container[image].ToString()))
    {
        using(MemoryStream memoryStream = new MemoryStream((byte[])container[image]))
        {
            string url = String.Format(@"{0}{1}{2}_{3}.jpg", EcoConfiguration.Instance.RootUrl, EcoConfiguration.Instance.SurveyImageRootUrl, surveyReference, image.SplitOnCapital("_"));

            using(FileStream fileStream = new FileStream(url, FileMode.Create, FileAccess.Write))
            {
                Dictionary<string, string> imageDetails = new Dictionary<string, string>();
                imageDetails.Add("TypeId", ((int)SurveyImageType.Exterior).ToString());
                imageDetails.Add("ImageUrl", url);
                if(container.ContainsKey(image + "Description"))
                {
                    imageDetails.Add("Description", container[image + "Description"].ToSafeString());
                }
                images.Add(imageDetails);
                await memoryStream.CopyToAsync(fileStream);
            }
        }
    }
}

Любые комментарии / предложения будут очень приветствоваться.


person markpirvine    schedule 27.02.2013    source источник


Ответы (1)


Сложность с файловыми потоками заключается в том, что вам нужно передать isAsync: true или FileOptions.Asynchronous методу конструктора / фабрики, чтобы получить действительно асинхронный поток. Если этого не сделать, то базовый файловый поток фактически блокируется, а асинхронные методы просто используют пул потоков для фальшивых асинхронных операций.

Еще одна вещь, которая выделяется в вашем коде, - это то, что у вас есть ненужное использование async. async следует использовать только тогда, когда он вам нужен. Например, этот метод:

private async Task ProcessProofOfPressenceImages(Dictionary<object, object> container, List<Dictionary<string, string>> images, string surveyReference)
{
  if(images != null)
  {
    await Task.WhenAll(this.ProcessImagesHelper(container, images, surveyReference, "propertyImage"));
  }
}

можно было бы записать как:

private Task ProcessProofOfPressenceImages(Dictionary<object, object> container, List<Dictionary<string, string>> images, string surveyReference)
{
  if(images != null)
  {
    return Task.WhenAll(this.ProcessImagesHelper(container, images, surveyReference, "propertyImage"));
  }

  return Task.FromResult<object>(null);
}

что избавляет вас от ненужного конечного автомата. Тот же совет применим к ProcessSketchImages и ProcessExteriorImages.

Что касается ProcessImagesHelper, он выглядит неплохо, хотя я не уверен, зачем вам MemoryStream. Было бы так же просто (асинхронно) записать массив байтов на диск.

Если вас интересуют async советы по повышению производительности, у Стивена Туба есть отличное видео.

person Stephen Cleary    schedule 27.02.2013
comment
Стивен, спасибо за ответ и совет - очень признателен - все асинхронные методы кажутся неправильными! Я изменил код, как было предложено, и устранил необходимость в MemoryStream и установил для параметра isAsync значение true. Я использовал другую статью (stackoverflow.com/questions/1862982/), чтобы определить оптимальный размер буфера для FileStream. - person markpirvine; 27.02.2013