Могут ли методы async иметь дорогой код перед первым "ожиданием"?
Неужели плохо иметь дорогостоящий код в начале метода асинхронного вызова, прежде чем вызывается первый await
? Должен ли этот код быть обернут с помощью TaskEx.Run
вместо?
public async Task Foo()
{
// Do some initial expensive stuff.
// ...
// First call to an async method with await.
await DoSomethingAsync;
}
Ответы
Ответ 1
Как говорит Рид, это действительно зависит от контекста. Код должен выполняться в какой-то момент, но в зависимости от контекста он может быть запущен в потоке пула потоков, а не в критическом.
Вместо использования Task.Run
я использовал бы TaskEx.Yield
:
public async Task Foo()
{
await TaskEx.Yield();
// Do expensive stuff
}
Насколько мне известно, это в основном способ немедленного возврата к вызывающему абоненту, но позволяет сразу же планировать остальную часть асинхронного метода. Если у вас что-то вроде потока пользовательских интерфейсов Windows Forms, нет смысла делать это, так как вы вернетесь к потоку пользовательского интерфейса (и запускаете дорогостоящий код там) сразу, но это будет иметь смысл, если вы находитесь в контексте где текущий поток не должен блокироваться, но продолжения выполняются в другом потоке.
Ответ 2
Это не обязательно плохо, но это может иметь неожиданные последствия. Если вызывающий абонент ожидает, что код будет вести себя полностью асинхронно, дорогой код будет работать синхронно. Это приведет к тому, что он будет вести себя как синхронный метод, но также асинхронно, что является наихудшим из обоих миров (дополнительная сложность от асинхронности без повторной записи...)
Если возможно, я бы порекомендовал попробовать иметь как можно меньше "дорогого" кода, ведущего к первому ожиданию. Использование Task.Run
(или TaskEx.Run
в CTP) для переноса дорогого кода или переноса дорогого кода в его собственный асинхронный метод (на котором вы могли бы await
) были бы полезны в этом случае.