Требуется ли метод «асинхронный», если только переадресовать вызов обработчику async? [Дубликат]

Вот простой способ отличить два списка (независимо от содержимого), вы можете получить результат, как показано ниже:

>>> from sets import Set
>>>
>>> l1 = ['xvda', False, 'xvdbb', 12, 'xvdbc']
>>> l2 = ['xvda', 'xvdbb', 'xvdbc', 'xvdbd', None]
>>>
>>> Set(l1).symmetric_difference(Set(l2))
Set([False, 'xvdbd', None, 12])

Надеюсь, это поможет.

161
задан TX_ 30 September 2013 в 16:30
поделиться

6 ответов

Выполняя иначе простой метод «thunk», async создает в памяти состояние async state machine, а не async - нет. Хотя это часто может указывать на использование версии non-async, поскольку она более эффективна (это правда), это также означает, что в случае зависания у вас нет доказательств того, что этот метод задействован в «стеке возврата / продолжения», что иногда затрудняет понимание повесы.

Итак, да, когда perf не критичен (и обычно это не так), я брошу async на все эти методы thunk, чтобы у меня была машина состояния async, чтобы помочь мне диагностировать зависания позже, и также, чтобы гарантировать, что, если эти методы thunk когда-либо будут развиваться со временем, они обязательно вернут сбитые задачи вместо throw.

10
ответ дан Andrew Arnott 21 August 2018 в 08:59
поделиться

Это также смущает меня, и я чувствую, что предыдущие ответы не учитывали ваш фактический вопрос:

Зачем использовать конструкцию return wait, когда вы можете напрямую возвращать задачу из внутреннего вызова DoAnotherThingAsync ()?

Ну, иногда вы на самом деле хотите Task<SomeType>, но в большинстве случаев вам действительно нужен экземпляр SomeType, то есть результат задачи.

Из вашего кода:

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

Человек, незнакомый с синтаксисом (например, я), может подумать, что этот метод должен возвращать Task<SomeResult>, но поскольку он помечен как async, это означает, что его фактический тип возврата SomeResult. Если вы просто используете return foo.DoAnotherThingAsync(), вы возвращаете задачу, которая не будет компилироваться. Правильный способ - вернуть результат задачи, поэтому return await.

2
ответ дан heltonbiker 21 August 2018 в 08:59
поделиться
  • 1
    «фактический тип возврата». А? async / await не меняет типы возвращаемых данных. В вашем примере var task = DoSomethingAsync(); вы получите задание, а не T – Shoe 5 January 2017 в 18:50
  • 2
    @Shoe, я не уверен, что хорошо понял async/await. Насколько я понимаю, Task task = DoSomethingAsync(), в то время как Something something = await DoSomethingAsync() работают. Первая дает вам правильную задачу, а вторая из-за ключевого слова await дает результат result из задачи после ее завершения. Я мог бы, например, иметь Task task = DoSomethingAsync(); Something something = await task;. – heltonbiker 5 January 2017 в 19:51

Единственная причина, по которой вы захотите это сделать, - это если в предыдущем коде есть еще один await или если вы каким-то образом обработаете результат перед его возвратом. Другой способ, которым это может происходить, - это try/catch, который изменяет порядок обработки исключений. Если вы ничего не делаете, тогда вы правы, нет причин добавлять накладные расходы на создание метода async.

20
ответ дан Servy 21 August 2018 в 08:59
поделиться
  • 1
    Как и в случае с ответом Стивена, я не понимаю, почему return await необходимо (вместо того, чтобы просто возвращать задачу вызова ребенка) , даже если в более раннем коде есть еще один ожидание. Не могли бы вы предоставить объяснение? – TX_ 30 September 2013 в 17:33
  • 2
    @TX_ Если вы хотите удалить async, то как бы вы ожидали первой задачи? Вам нужно отметить метод как async, если вы хотите использовать any . Если метод отмечен как async, и у вас есть await ранее в коде, тогда вам нужно await выполнить вторую операцию async для правильного типа. Если вы просто удалили await, тогда он не будет компилироваться, так как возвращаемое значение не будет иметь правильный тип. Поскольку метод async, результат всегда завернут в задачу. – Servy 30 September 2013 в 17:38
  • 3
    @Noseratio Попробуйте два. Первый компилируется. Второе - нет. Сообщение об ошибке сообщит вам о проблеме. Вы не будете возвращать правильный тип. Когда в методе async вы не возвращаете задачу, вы возвращаете результат задачи, которая затем будет завернута. – Servy 30 September 2013 в 20:21
  • 4
    @Servy, конечно, вы правы. В последнем случае мы возвращаем Task<Type> явно, а async диктует возвратить Type (который сам компилятор превратится в Task<Type>). – Noseratio 30 September 2013 в 20:33
  • 5
    @Itsik Ну конечно, async является просто синтаксическим сахаром для явной проводки продолжений. Вы не нуждаетесь async, чтобы что-либо сделать, но при выполнении практически нетривиальной асинхронной операции с резко легче работать. Например, код, который вы предоставили, на самом деле не распространяет ошибки, как вы этого хотели, и сделать это правильно в еще более сложных ситуациях становится намного сложнее. В то время как вы никогда не нуждаетесь async, ситуации, которые я описываю, это то, где он добавляет ценность, чтобы использовать его. – Servy 26 February 2016 в 03:33

Если вам не нужно async (т. е. вы можете напрямую вернуть Task), тогда не используйте async.

Есть ситуации, когда return await полезно, например, если у вас есть две асинхронные операции :

var intermediate = await FirstAsync();
return await SecondAwait(intermediate);

Дополнительные сведения о производительности async см. в статье MSDN Стивена Тоуба и video по этой теме.

Обновление: я написал сообщение в блоге , которое идет гораздо подробнее.

53
ответ дан Stephen Cleary 21 August 2018 в 08:59
поделиться
  • 1
    Не могли бы вы объяснить, почему await полезен во втором случае? Почему бы не сделать return SecondAwait(intermediate);? – Matt Smith 30 September 2013 в 17:15
  • 2
    У меня такой же вопрос, как у Мэтта, не будет ли return SecondAwait(intermediate); достичь цели в этом случае? Я думаю, return await здесь лишний ... – TX_ 30 September 2013 в 17:31
  • 3
    @MattSmith Это не скомпилируется. Если вы хотите использовать await в первой строке, вы должны использовать его и во втором. – svick 30 September 2013 в 21:29
  • 4
    @cateyes Я не уверен, что означает «накладные расходы, связанные с их параллелизмом», но версия async будет использовать ресурсы меньше (потоков), чем ваша синхронная версия. – svick 17 July 2014 в 10:29
  • 5
    @TomLint Он действительно не компилируется. Предполагая, что тип возвращаемого значения SecondAwait является строкой, сообщение об ошибке: «CS4016: Поскольку это метод асинхронизации, выражение возврата должно быть type 'string' вместо 'Task & lt; string & gt;' & quot ;. – svick 23 November 2016 в 13:17

Есть один подлый случай, когда return в обычном методе и return await в async метод ведут себя по-разному: в сочетании с using (или, более общо, любой return await в блоке try), .

Рассмотрим эти две версии метода:

Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return foo.DoAnotherThingAsync();
    }
}

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

Первый метод будет Dispose() объектом Foo, как только метод DoAnotherThingAsync() вернется, что вероятно, задолго до его завершения. Это означает, что первая версия, вероятно, глючит (потому что Foo находится слишком рано), а вторая версия будет работать нормально.

122
ответ дан svick 21 August 2018 в 08:59
поделиться
  • 1
    Для полноты, в первом случае вы должны вернуть foo.DoAnotherThingAsync().ContinueWith(_ => foo.Dispose()); – ghord 23 September 2014 в 09:58
  • 2
    @ghord Это не сработает, Dispose() возвращает void. Вам понадобится что-то вроде return foo.DoAnotherThingAsync().ContinueWith(t -> { foo.Dispose(); return t.Result; });. Но я не знаю, почему бы вам это сделать, когда вы можете использовать второй вариант. – svick 23 September 2014 в 10:05
  • 3
    @svick Вы правы, это должно быть больше вдоль линий { var task = DoAnotherThingAsync(); task.ContinueWith(_ => foo.Dispose()); return task; }. Вариант использования довольно прост: если вы используете .NET 4.0 (как и большинство), вы все равно можете написать асинхронный код таким образом, который будет хорошо работать из 4.5 приложений. – ghord 23 September 2014 в 12:38
  • 4
    @ghord Если вы находитесь на .Net 4.0 и хотите написать асинхронный код, вероятно, вы должны использовать Microsoft.Bcl.Async . И ваш код имеет Foo только после того, как завершено возвращенное Task, что мне не нравится, потому что оно излишне вводит параллелизм. – svick 23 September 2014 в 13:47
  • 5
    @svick Ваш код ждет, пока задача не будет закончена. Кроме того, Microsoft.Bcl.Async непригодна для меня из-за зависимости от KB2468871 и конфликтов при использовании асинхронной кодовой базы .NET 4.0 с правильным асинхронным кодом 4.5. – ghord 23 September 2014 в 16:06
11
ответ дан Andrew Arnott 1 November 2018 в 03:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: