Подтвердить что ты не робот

Await/async против "классического" асинхронного (обратные вызовы)

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

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

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

  • Можно ли предложить лучшую производительность по сравнению с другой?
  • Есть ли накладные расходы на тот, который больше, чем другой?
  • Что лучше использовать в высокопроизводительной среде?
4b9b3361

Ответ 1

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

Контекст .NET.NET. Мы намерены и ждать, и ContinueWith (...) захватывать и восстанавливать .NET Execution Context. В противном случае это не изменило бы требования безопасности .NET, потому что тогда вы сможете использовать произвольные вещи, такие как учетные данные и т.д., И оставить их в потоковом потоке для следующего рабочего элемента. Для "ожидания" это настройка, которую мы сделали во внутренних сборках, но это было после того, как мы подготовили предварительный просмотр проекта //BUILD.

Выделение памяти: несколько способов "ожидать" лучше выделять память, чем ручные обратные вызовы. Ключ в том, что для функций с множеством ожиданий то, что вы действительно генерируете, эквивалентно нескольким обратным вызовам. Если у вас есть 5 ожиданий в линейном порядке выполнения, и при выполнении всегда идет до конца, тогда эквивалент потребует 5 обратных вызовов. Для каждого из этих 5 обратных вызовов можно создать отдельный объект закрытия лямбда и делегат, который представляет эту конкретную лямбда. В случае "ожидание" компилятор знает, что вы не собираетесь использовать объект делегирования для чего-либо еще. Таким образом, вместо этого весь метод разделяет 1 закрытие и 1 делегат, причем внутренний конечный автомат отслеживает, где вы находитесь внутри метода. Таким образом, для этого случая "ожидание" выделяет меньше объектов, что фактически может ускорить вашу программу, поскольку слишком много объектов = больше времени, которое GC должен потратить на выяснение того, что живое/мертвое.

Сокращение "Ожидание" также имеет более благоприятную семантику, чем просто обратные вызовы. В случае, когда вы создаете лямбда обратного вызова, компилятор вынужден выделять закрытие и делегат точки входа лямбда независимо от того, что. Для "ожидания" договор ожидания предполагает более оптимизированную кодировку для ожидающих, которые уже "сделаны". если ожидаемый говорит, что это "сделано" до того, как ожидание будет оценено, семантика - это всего лишь чистый проход от выдергивания результата. Это означает, что у компилятора есть возможность отложить выделение до тех пор, пока он вам не понадобится, и, таким образом, вы никогда не будете оплачивать распределение закрытия, делегировать распределение или расходы на планирование, если только вам это не понадобится. В текущем компиляторе предварительного просмотра разработчика эти оптимизации производительности.

Торговая опасность для perf Если вы действительно хотите обойти модель безопасности .NET, вы могли бы представить себе случай, когда вы можете получить немного перфоманс, избегая реализации контекстного пакета/восстановления, если вы абсолютно уверены, что вам никогда не понадобится захват/восстановление контекста. Однако большинство методов .NET будут делать это тихо под обложками, поэтому вам действительно нужно знать, какие из них предоставят вам необработанный доступ без него. Эмпирическое правило для .NET заключается в том, что если API доступен с частичным доверием (например, Silverlight), тогда API, безусловно, захватывает контекст при вызове и затем восстанавливает его, если он API, который переводит выполнение в другое место (например, ContinueWith, QueueUserWorkItem (...), и т.д.). Если вы катите свой собственный threadpool, который просто помещает в очередь делегатов, вы можете обойти это, но, скорее всего, вам это не понадобится.

Моя личная рекомендация Используйте ожидание. Это более высокий уровень, и это то, что вы хотите. Мы приложили немало усилий, пытаясь настроить его для этого выпуска, и мы могли бы, вероятно, настроить его дальше. API-интерфейсы с обратным вызовом будут более ограниченными, потому что компилятор может так настроить только до того, как они начнут нарушать языковые правила. Ожидание в методе позволяет вам иметь более умные закрытия, чем обратные вызовы. И... ожидание намного более интуитивно понятное для чтения/использования, чем обратные вызовы:)

Ответ 2

Подобно анонимным функциям и итераторам, ключевые слова async и await являются синтаксическим сахаром. В техническом смысле они не более или менее эффективны, чем эквивалентные не-сахаристые версии. Они просто сэкономит вам много информации.