Рекомендации по использованию await-async, с чего начать задачу?

Я начал использовать механизм wait/async в нашем.Net приложении WPF.

В моей модели ViewModel я вызываю метод async для службы.

Мой вопрос: лучше ли

  1. Непосредственно внутри этой службы сделайте одно большое return await Task.Run(()=>{...});
  2. Пусть все подтемы на этой службе также будут асинхронны, а затем внутри этого есть Task.Run?

Например:

1)

public class Service:IService{
    public async Task<SomeResult>(SomeParameter parameter){
        return await Task.Run(()=>{
            CopyStuff(parameter.A);
            UpgradeStuff(parameter.B);
            return ReloadStuff(parameter.C)
        });
    }

    private void CopyStuff(ParamA parameter){
        ...//Some long operation that will mainly wait on the disk

    }
    private void UpgradeStuff(ParamB parameter){
        ...//Some long operation that should not block the GUI thread
    }
    public SomeResult ReloadStuff(ParamC parameter){
        return ...;//Some long operation that relaunch some services and return their successs      
    }   
}

2)

public class Service:IService{
    public async Task<SomeResult>(SomeParameter parameter){
        await CopyStuff(parameter.A);
        await UpgradeStuff(parameter.B);
        return await ReloadStuff(parameter.C)       
    }

    private async Task CopyStuff(ParamA parameter){
        return await Task.Run(()=>{...});//Some long operation that will mainly wait on the disk
    }
    private async Task UpgradeStuff(ParamB parameter){
        return await Task.Run(()=>{...});//Some long operation that should not block the GUI thread
    }
    public async Task<SomeResult> ReloadStuff(ParamC parameter){
        return await Task.Run(()=>{return ...});//Some long operation that relaunch some services and return their successs 
    }   
}

Я вижу преимущества в обоих подходах:

  • В 1) мы будем использовать меньшую задачу, это, вероятно, наиболее эффективно (???)
  • В 2) Это кажется более "совместимым" с подходом асинхронного ожидания, это позволит изменить видимость некоторых методов и все еще быть асинхронным, это позволит параллельным методам запуска, если потребуется, в один прекрасный день.

Ответ 1

Какой вариант выбрать?

Я бы не использовал ни один из ваших вариантов, оба из них создадут вводящий в заблуждение API, все, кто будет использовать вашу службу, будут думать, что он использует асинхронные методы, но правда в том, что за ложной подписью методы на самом деле не асинхронны вообще.
Ваш сервис просто подталкивает работу к другому потоку ThreadPool который будет заблокирован во время выполнения метода.

Хотя на стороне клиента, которая не звучит так плохо на стороне сервера, используя этот принцип, действительно может повредить вашу масштабируемость.

По словам Стивена Клири:

не используйте Task.Run в реализации метода; вместо этого используйте Task.Run для вызова метода.

Вы не должны обертывать свои методы обслуживания поддельными подписями async, если методы действительно синхронны, если вы не хотите блокировать поток пользовательского интерфейса во время выполнения тяжелого метода, вы должны использовать Task.Run, когда вы вызываете методы служб из модель представления.

Я предлагаю вам прочитать статью Стивена Клири " Задачи". Этикетки в блоге.

Рассмотрите возможность использования асинхронных методов для операций ввода-вывода

Более того, я вижу, что работа, которую выполняет ваша служба, связана не только с работой процессора, поэтому вам следует рассмотреть возможность использования встроенного метода асинхронного ввода-вывода I/O, если есть доступный по сравнению с синхронным, который вы используете сейчас (например, Asynchronous File I/O), в этом случае ваши методы будут истинными асинхронными методами, а не фальшивыми обертками async, как сейчас.

Если вы это сделаете, ваш поток пользовательского интерфейса не будет блокироваться, пока будет выполняться операция асинхронного ввода-вывода, но если есть еще и тяжелая работа по работе с ЦП, и вы не хотите блокировать пользовательский интерфейс во время выполнения работы с ЦП, вы можете все еще используйте Task.Run, когда вы вызываете метод службы из модели представления (хотя подпись метода уже является асинхронной).

Подробнее о смеси синхронных и асинхронных методов в серии статей выше.

Еще одним большим преимуществом использования методов async API является то, что если метод действительно асинхронный, вы не блокируете ни один из потоков ThreadPool пока выполняется операция ввода-вывода async, и больше потоков ThreadPool могут выполнять любую другую работу.
Это особенно важно (но не только) важно при использовании асинхронного программирования на стороне сервера, и это может реально повысить вашу масштабируемость.

Async и MVVM

Последнее, если вы следуете шаблону MVVM. MSDN "async MVVM" статьи - отличный материал для чтения, который вы можете использовать.