Запускать и забывать асинхронный метод задачи иногда не вызывать

У меня есть асинхронный метод:

public async Task DoSomethingAsync(){
    ...
    await ...
    await ...
    ....
    return await SaveAsync();
}

В большинстве случаев я вызываю этот метод следующим образом:

await DoSomethingAsync()

Этот звонок работает как положено. Но где-то мне нужно вызвать этот метод как fire и забыть:

public void OtherMethod()
{
    ...
    DoSomethingAsync(); //fire and forget here
}

В этом случае иногда Задача DoSomethingAsync() выполняется и завершается, но иногда задача никогда не вызывается (или вызывать некоторые awaitв пределах DoSomethingAsync() но никогда не завершать последний await SaveAsync();).

Я пытаюсь удостовериться, что задача будет вызвана в огне, и забуду этим способом:

public void OtherMethod()
{
    ...
    Task.Factory.StartNew(() =>
    {
        await DoSomethingAsync();
    }); //fire and forget again here
}

Однако это не работает как ожидание. Итак, мои вопросы:

  1. Как позвонить DoSomethingAsync() без await всегда будет работать и завершиться? (Меня не волнует случай перезапуска / сбоя AppDomain)

  2. Если я уберу все async/await код внутри DoSomethingAsync() и заменить await от .ContinueWith()затем вызов Task DoSomethingAsync() (не иметь async в объявлении метода) будет вызван и обязательно завершен (игнорировать случай перезапуска / сбоя AppDomain), если да, то как долго после вызова (я не думаю, что я буду счастлив, если Задача будет вызвана через 10 минут)?

3 ответа

Решение

Ожидания должны работать нормально, так что, вероятно, здесь происходит что-то еще, что задерживает один или несколько ожидаемых вами методов. Есть несколько возможностей:

  1. У вас есть тупик где-то в одном из ваших методов, препятствующий его завершению и блокирующий ожидание от возобновления.
  2. Все потоки вашего пула потоков по какой-то причине блокируются, что не позволяет запустить ожидающие задачи.
  3. Вы запускаете асинхронный метод в контексте синхронизации, и что-то удерживает контекст, не позволяя ему запустить отправленные обратные вызовы. Обычно контекстом является поток пользовательского интерфейса, и, как правило, это довольно очевидно, поскольку он блокирует пользовательский интерфейс вашего приложения.

Если вы можете подключиться с помощью отладчика VS и наблюдать за происходящим, попробуйте остановиться и посмотреть на представление Parallel Stacks. Это должно помочь сузить возможности для рассмотрения.


Как отметил Стивен, также возможно, что Исключение происходит, когда вы называете его "забыл и забыл". Если вы этого еще не сделали, убедитесь, что вы обрабатываете TaskScheduler.UnobservedTaskException, чтобы регистрировать любые подобные события. Обратите внимание, что это вызывается из финализатора, поэтому время его вызова недетерминировано. Это может усложнить отладку, поскольку событие может не сработать, пока не произойдет фактическое событие, вызвавшее исключение. Поэтому я рекомендую последовать совету Стивена и сохранить Задание, чтобы ждать или Ждать где-нибудь еще позже.

Вы, вероятно, получаете исключение где-то в DoSomethingAsync, который вы не можете наблюдать, потому что вы игнорируете задачу. Это именно то поведение, о котором вы просите, поскольку вы говорите коду "забыть" задачу.

Чтобы соблюсти исключение, нельзя "забыть" задание:

public Task OtherMethodAsync()
{
  ...
  return DoSomethingAsync();
}

И в какой-то момент, await (или же Wait) возвращенное задание. Вот как вы знаете, задача будет выполняться и завершаться.

ASP.NET, как правило, не позволяет запускать операции "забей и забудь" (async void) из запроса. См. /questions/47299461/configureawait-kogda-ne-zhdet/47299511#47299511 для дальнейшего объяснения этого поведения и некоторых потенциальных обходных путей.

Другие вопросы по тегам