Есть ли обратная сторона в использовании асинхронных? - PullRequest
0 голосов
/ 14 октября 2018

Допустим, у меня есть асинхронный метод, который использует Entity Framework для извлечения данных из базы данных.Например:

public async Task<MyEntity> Get(string bar){
    return await db.MyEntities.Where(x=>x.Foo==bar).SingleOrDefaultAsync();
}

Это в основном будет использоваться асинхронным контроллером MVC или Web API, и это прекрасно работает.Но, возможно, я также иногда хочу использовать его в консольном приложении, которое не имеет асинхронного метода.Я могу сделать это, используя Task.Run

private static async Task MainAsync()
{
    using(MyEntityService repo=new MyEntityService())
    {
        MyEntity myEntity=await repo.Get("foobar");

        //do stuff
    }
}

public static void Main()
{
    Task.Run(async () =>
    {
        await MainAsync();
    }).Wait();
}

Мой вопрос: есть ли у этого недостаток по сравнению с реализацией не асинхронной версии метода Get?Предполагая, что я доволен этим уровнем сложности кода, есть ли причины производительности, если не асинхронный метод может быть лучше?

Ответы [ 2 ]

0 голосов
/ 14 октября 2018

Что ж, не асинхронный метод будет лучше, если в вашем действии нет операций, связанных с вводом / выводом (например, чтение с базы данных / диска или сети), потому что асинхронизация / ожидание имеет дополнительные дополнительные затраты.В противном случае асинхронное действие может быть хорошим выбором из-за освобождения потока IIS

0 голосов
/ 14 октября 2018

Прежде чем я попытаюсь дать что-то, что напоминает ответ на ваш вопрос, я чувствую, что мне нужно, чтобы вы знали о важной проблеме, и это то, что в C # 7.1 или выше вы можете использовать асинхронный метод Main.Вы можете прочитать больше об этом в Что нового в C # 7.1 , но TL; DR, после переключения вашего проекта на C # 7.1 или выше, или "последней версии", вы можете сделать это:

public class Program
{
    public static async Task Main(string[] args)
    {
        using(MyEntityService repo=new MyEntityService())
        {
            MyEntity myEntity=await repo.Get("foobar");

            //do stuff
        }
    }
}

Теперь давайте попробуем ответить на ваш вопрос.Имейте в виду, что мои знания об асинхронности / ожидании могут быть ошибочными / неполными, поэтому могут появиться другие ответы, которые лучше справляются с этой задачей.

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

Каждый раз, когда вы объявляете метод как async, компиляция всего метода изменяется.Все это превращается в конечный автомат и переписывается и перемещается.Если впоследствии вы декомпилируете свою программу, если декомпилятор действительно продвинутого типа, декомпилированный код будет выглядеть не так, как ваша оригинальная программа.

Теперь этот конечный автомат добавит заметные накладные расходы к выполнению программы, с точки зрениявремя выполнения или использование памяти?Нет, не совсем.

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

Один изПроблемы, с которыми вам придется иметь дело, это когда речь идет об исключениях.Трассировка стека становится ... интересной ... когда вы задействуете методы, объявленные как async.

Например, с учетом этой короткой программы (которую я запускал с использованием LINQPad ):

async Task Main()
{
    await Test1();
}

public static async Task Test1()
{
    await Task.Delay(1000);
    throw new Exception("Test");
}

Можно ожидать, что трассировка стека исключительной ситуации будет содержать Main, но, увы, из-за того, что оператор throw выполняется после того, как задача «всплыла» после задержки, она будетвыполняется каким-то фреймворковым кодом, и трассировка стека на самом деле выглядит следующим образом:

   at UserQuery.<Test1>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at UserQuery.<Main>d__0.MoveNext()

Я думаю, что вы действительно ищете некоторые рекомендации о том, как работать с асинхронным кодом, и лучшиймогут сделать следующее:

  • Если вы пишете синхронный код и имеете дело с API, который имеет синхронные методы, используйте синхронные методы
  • Если вы пишете асинхронный код,и для работы с API, который имеет асинхронные методы, используйте асинхронные методы
  • Если вы пишете синхронный код, и для работы с API, который имеет асинхронные методы, используйтеасинхронные методы, используйте .Wait() или .Result, чтобы получить результат и точно знать что вы делаете
  • Если вы пишете асинхронный код,и имея дело с API, который имеет синхронные методы, используйте асинхронные методы

(Имейте в виду, что я никоим образом не говорил о , что методыAPI делали;я предполагаю, что если API предоставляет асинхронные методы, то для этого есть причина)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...