Async/ждут высокопроизводительные серверные приложения?

новые ключевые слова async/await на С# 5 выглядят очень многообещающими, но я прочитал статью о влиянии производительности на эти приложения, поскольку компилятор будет генерировать довольно сложный конечный автомат для методов async.

Асинхронное программирование с использованием этих ключевых слов намного проще, но так же хорошо, как сказать SocketAsyncEventArgs для сокетов?

Второй вопрос: являются ли асинхронные методы ввода-вывода, такие как Stream.WriteAsync, действительно асинхронными (Completion Ports on.NET или epoll/poll на Mono), или эти методы являются дешевыми оболочками для нажатия вызова записи в threadpool?

Третий вопрос: помимо SynchronizationContext приложения UI, существует ли способ реализовать какой-то контекст с сингл-потоком? Что-то вроде цикла событий, чтобы конечные задачи продолжались в основном потоке? Я обнаружил библиотеку Nito.AsyncEx, но я не совсем уверен, действительно ли это мне нужно.

Ответы

Ответ 1

async сам по себе довольно совершенен. В этом много работы.

В общем, на стороне сервера вас беспокоит async I/O. Я проигнорирую async методы с привязкой к ЦП, потому что накладные расходы async все равно будут потеряны.

Асинхронный ввод-вывод увеличит использование вашей памяти в каждом запросе, но это уменьшит потребление потоков для каждого запроса. Таким образом, вы в конечном итоге выигрываете (кроме пограничных патологических угловых случаев). Это справедливо для всех асинхронных операций ввода-вывода, включая async.

await был разработан с шаблоном, а не только с типом Task, поэтому, если вам нужно выжать как можно больше производительности, вы можете.

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

статья, которую вы прочитали Стивеном Тубом, превосходна. Я также рекомендую Zen Async video (также Стивен Туб).

Асинхронное программирование с использованием этих ключевых слов намного проще, но так же хорошо, как сказать SocketAsyncEventArgs для сокетов?

Во-первых, поймите, что SocketAsyncEventArgs является более масштабируемым, поскольку он уменьшает количество мусора. Более простой способ использования сокетов async будет генерировать больше мусора памяти, но поскольку await основан на шаблонах, вы можете определить свои собственные async -компьютерные обертки для API SocketAsyncEventArgs (как видно на блоге Стивена Тууба... Я чувствую шаблон здесь;). Это позволяет вам выжимать каждую унцию производительности.

Хотя в конечном итоге обычно лучше разрабатывать масштабируемую систему, а не скручивать код, чтобы избежать небольшого выделения памяти. ИМХО.

Второй вопрос: являются ли асинхронные методы ввода-вывода, такие как Stream.WriteAsync, действительно асинхронными (Completion Ports on.NET или epoll/poll на Mono), или эти методы являются дешевыми оболочками для нажатия вызова записи в threadpool?

Я не знаю о Моно. В .NET большинство асинхронных методов ввода-вывода основаны на порте завершения. Класс Stream является заметным исключением. Базовый класс Stream по умолчанию будет делать "дешевую упаковку", но позволяет производным классам переопределять это поведение. Stream, которые поступают из сетевых коммуникаций, всегда переопределяют это, чтобы обеспечить действительно асинхронный ввод-вывод. Stream, которые обрабатывают файлы только для переопределения этого , если, поток был сконструирован явно для асинхронного ввода-вывода.

Третий вопрос: помимо SynchronizationContext приложения UI существует ли способ реализовать какой-то однопоточный контекст?

ASP.NET также имеет SynchronizationContext, поэтому, если вы используете ASP.NET, вы уже настроены.

Если вы выполняете собственный сервер на основе сокетов (например, службу Win32), вы можете использовать тип AsyncContext в моей библиотеке AsyncEx. Но это звучит не так, как хотелось бы. AsyncContext создаст однопоточный контекст для текущего потока. Но истинная мощность async для серверных приложений исходит из запросов масштабирования вместо потоков.

Рассмотрим, как работает ASP.NET SynchronizationContext: по мере поступления каждого запроса он захватывает поток пула потоков и строит SynchronizationContext (для этого запроса). Когда этот запрос выполняет асинхронную работу, он регистрируется с помощью SynchronizationContext, а поток, выполняющий этот запрос, возвращается в пул потоков. Позже, когда асинхронная работа завершается, она захватывает поток пула потоков (любой поток), устанавливает на нем существующий SynchronizationContext и продолжает обрабатывать этот запрос. Когда запрос завершен, его SynchronizationContext размещается.

Ключом в этом процессе является то, что когда ожидание запроса (await) асинхронных операций, нет потоков, посвященных этому запросу. Поскольку запрос значительно облегчен по сравнению с потоком, это позволяет серверу масштабироваться лучше.

Если вы предоставили каждому из ваших запросов однопоточное SynchronizationContext, например AsyncContext, это привязало бы поток к каждому запросу, даже если ему нечего делать. Это вряд ли лучше, чем синхронный многопоточный сервер.

Вы можете найти мою статью async voidи await, поэтому вам не придется делать это явно.

Ответ 2

  • Если вы используете его в контексте async IO, это спорный вопрос. Время, затрачиваемое на работу с базой данных, файл/сеть IO и т.д., В лучшем случае будет миллисекунд. Накладные расходы async будут в худшем случае микросекундами, если не наносекунды. Где вам нужно быть осторожным, когда у вас много операций, которые вы ожидаете (как в тысячах, десятках тысяч и более), так и в тех операциях, которые очень быстры. Когда эти операции async представляют собой работу с ЦП, конечно, возможно, что накладные расходы при использовании await должны быть как минимум заметными. Обратите внимание, что код, сгенерированный для конечного автомата, несколько сложный, с точки зрения способности человека понимать, но государственные машины в целом имеют тенденцию работать довольно хорошо как целое.

  • Методы - это не только обертки, которые блокируют поток пула потоков, нет. Это победит цель того, что представляет await. Эти методы не блокируют поток и полагаются на крючки ОС для выполнения задачи.

  • Конечно, вы можете создать свой собственный SynchronizationContext вместо того, чтобы полностью полагаться на существующий, предоставленный вашей инфраструктурой пользовательского интерфейса. Здесь - отличный пример. Просто будьте осторожны при использовании чего-то подобного; это хороший инструмент для правильной задачи, но можно злоупотреблять, чтобы найти более эффективный способ блокировки, когда вы должны просто делать все асинхронно.