Запустите и забудьте, используя `Task.Run` или просто вызывая метод asyn c без` await` - PullRequest
2 голосов
/ 20 марта 2020

Примечание. Это вопрос вне сферы действия Asp.Net и веб-приложений.

В целом, особенно когда речь идет о библиотеках или консольных приложениях, чтобы запустить и забыть асинхронный c метод, лучше просто вызвать метод asyn c без await его использования или использовать Task.Run?

В основном:

public static void Main(){
  // Doing
  _ = DoSomethingAsync();

  // vs.
  _ = Task.Run(DoSomethingAsync);
}

private static async Task DoSomethingAsync()
 {
   ...
 }

Ответы [ 2 ]

3 голосов
/ 20 марта 2020

В целом, особенно когда речь идет о библиотеках или консольных приложениях, для того, чтобы запустить и забыть метод asyn c, лучше просто вызвать метод asyn c, не ожидая его, или использовать Task. Беги?

В общем, лучше вообще не использовать огонь и забыть.

«Огонь и забудь» означает:

  1. Вы не волнует, есть ли в коде исключение. Любые исключения приведут к молчанию сбоев; без регистрации, без уведомлений и т. д. c.
  2. Вам не нужно знать, когда код завершится. Т.е. приложению-потребителю не нужно ждать завершения кода. Даже во время выключения.
  3. Вам не нужен код для завершения. Как следствие (2), код «забей и забудь» может не выполняться до конца; и как следствие (1), у вас не было бы уведомления о том, что он не был выполнен.

Короче говоря, «увольняйся» подходит только для очень маленького количество заданий Например, обновление кеша. Я бы сказал, что, вероятно, 85% или более кода «запусти и забудь» - это неправильно - он использует огонь и забывает для кода, который должен не быть огнем и забыть.

Так что я бы сказал, что лучшее решение - вообще не использовать огонь и забыть. По крайней мере , вы должны выставить где-нибудь Task, что представляет собой "последующее действие". Попробуйте добавить Task к вашему типу возврата или выставить его как свойство.

Принятие огня и забывание - особенно в библиотеке - означает, что вы заставляете всех потребителей никогда не знать, когда безопасно завершить работу и Выход. Но если вы действительно хотите сделать огонь и забыть, есть несколько вариантов.

A. Одним из вариантов является вызов async void функции без контекста . Приложение-потребитель по-прежнему не может определить, завершен ли код, но, по крайней мере, исключения из этого способа не игнорируются.

B. Другой вариант - запустить задачу без контекста. У этой опции есть оба недостатка: запускать и забывать код: исключения игнорируются, и вызывающий код не может знать, когда он завершится.

Обе эти рекомендации запускают задачу без контекста. Есть помощников для выполнения этого , или вы можете заключить вызов в Task.Run (немного менее эффективно, но он работает нормально).

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

0 голосов
/ 20 марта 2020

В общем, это зависит, но Task.Run является более безопасным выбором, и причины этого зависят от двух вещей:

  1. Если мы можем гарантировать, что метод async всегда будет асинхронный. Гарантия того, что метод всегда будет асинхронным, может стать особенно сложной для библиотечных проектов или если метод asyn c находится в коде, который не управляется вызывающей стороной. Например, однажды я реализовал метод, подобный этому примеру, в библиотеке:
 public async Task HandleMessages(Func<Task> onMessageNotification)
 {
   while(true)
   {
      var msg = MessagingClient.ReceiveMessage();
      ...
     if(msg.MessageType == "MakeAPizza")
     { 
       _ = onMessageNotification();
       _ = MakePizzaAsync();
     }
   }
 }

 private async Task MakePizzaAsync() {...}

Я хотел запустить и забыть onMessageNotification и MakePizzaAsync, так как не хотел, чтобы он удерживался обработка сообщений. То, что я не уловил, было, когда вызывающий оператор реализовал onMessageNotification, как показано ниже:

Func<Task> onMsgNotification = () => {
  DoSlowOperation();
  return Task.CompletedTask;
}

, который фактически сделал бы синхронную операцию onMessageNotification, и должен был ждать от DoSlowOperation() до конца sh. Поэтому, чтобы избежать решения проблемы, мне просто нужно изменить ее на:

  _ = Task.Run(onMessageNotification);
  _ = MakePizzaAsync();
У нас не будет длительных операций в методе asyn c до первой операции await. Другой способ, которым вызывающая сторона может вызвать проблемы, - это если onMsgnotification Fun c были реализованы следующим образом:
Func<Task> onMsgNotification = async () => {
  DoSlowOperation();
  await AsyncOperation();
}

, что все равно замедлило бы метод HandleMessages, потому что DoSlowOperation все еще вызывается синхронно.

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