У меня всегда было впечатление, что использование ThreadPool для (скажем, некритичных) краткосрочных фоновых задач считалось лучшей практикой даже в ASP.NET, но потом я наткнулся на эту статью, которая, кажется, предполагает иное - аргументом является то, что вы должны оставить ThreadPool для обработки запросов, связанных с ASP.NET.
Итак, вот как я до сих пор выполнял небольшие асинхронные задачи:
ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))
И статья предлагает вместо этого явно создать поток, например:
new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()
Преимущество первого метода состоит в том, что он управляем и ограничен, но есть вероятность (если статья верна), что фоновые задачи затем соперничают за потоки с обработчиками запросов ASP.NET. Второй метод освобождает ThreadPool, но за счет того, что он неограничен и, следовательно, потенциально использует слишком много ресурсов.
Итак, мой вопрос: правильный ли совет в статье?
Если ваш сайт получает так много трафика, что ваш ThreadPool заполняется, тогда лучше выйти за пределы диапазона, или полный ThreadPool будет означать, что вы все равно исчерпываете свои ресурсы, и в этом случае вы не следует пытаться начинать свои собственные темы?
Уточнение: я просто спрашиваю о небольших некритических асинхронных задачах (например, удаленное ведение журнала), а не о дорогостоящих рабочих элементах, которые требуют отдельного процесса (в этих случаях я согласен, что вам понадобится более надежное решение).
источник
Ответы:
Другие ответы здесь, похоже, не учитывают самый важный момент:
Если вы не пытаетесь распараллелить операцию, интенсивно использующую ЦП, чтобы ускорить ее выполнение на сайте с низкой нагрузкой, нет никакого смысла использовать рабочий поток вообще.
Это касается как бесплатных потоков, созданных
new Thread(...)
, так и рабочих потоковThreadPool
, отвечающих наQueueUserWorkItem
запросы.Да, это правда, вы можете заморозить
ThreadPool
процесс ASP.NET, поставив в очередь слишком много рабочих элементов. Это предотвратит обработку дальнейших запросов ASP.NET. Информация в статье верна в этом отношении; тот же пул потоков используется дляQueueUserWorkItem
обслуживания запросов.Но если вы действительно ставите в очередь достаточно рабочих элементов, чтобы вызвать голод, тогда вам следует истощить пул потоков! Если вы выполняете буквально сотни операций с интенсивным использованием ЦП одновременно, какой смысл иметь другой рабочий поток для обслуживания запроса ASP.NET, когда машина уже перегружена? Если вы столкнулись с такой ситуацией, вам нужно полностью переделать дизайн!
В большинстве случаев я вижу или слышу о том, что многопоточный код используется ненадлежащим образом в ASP.NET, это не для постановки в очередь работы, интенсивно использующей процессор. Это для постановки в очередь работы, связанной с вводом-выводом. И если вы хотите выполнять операции ввода-вывода, вам следует использовать поток ввода-вывода (порт завершения ввода-вывода).
В частности, вы должны использовать асинхронные обратные вызовы, поддерживаемые любым классом библиотеки, который вы используете. Эти методы всегда очень четко обозначены; они начинаются со слов
Begin
иEnd
. Как и вStream.BeginRead
,Socket.BeginConnect
,WebRequest.BeginGetResponse
, и так далее.Эти методы действительно используют
ThreadPool
, но они используют IOCP, которые не мешают запросам ASP.NET. Это особый вид облегченного потока, который может быть «разбужен» сигналом прерывания от системы ввода-вывода. А в приложении ASP.NET у вас обычно есть один поток ввода-вывода для каждого рабочего потока, поэтому для каждого отдельного запроса может быть поставлена в очередь одна асинхронная операция. Это буквально сотни асинхронных операций без значительного снижения производительности (при условии, что подсистема ввода-вывода может не отставать). Это намного больше, чем вам когда-либо понадобится.Просто имейте в виду, что асинхронные делегаты не работают таким образом - они в конечном итоге будут использовать рабочий поток, как и
ThreadPool.QueueUserWorkItem
. На это способны только встроенные асинхронные методы классов библиотеки .NET Framework. Вы можете сделать это сами, но это сложно и немного опасно и, вероятно, выходит за рамки этого обсуждения.На мой взгляд, лучший ответ на этот вопрос - не использовать
ThreadPool
или фоновыйThread
экземпляр в ASP.NET . Это совсем не похоже на запуск потока в приложении Windows Forms, где вы делаете это, чтобы пользовательский интерфейс оставался отзывчивым и не заботился о том, насколько он эффективен. В ASP.NET вас беспокоит пропускная способность , и все эти переключения контекста для всех этих рабочих потоков абсолютно убивают вашу пропускную способность, независимо от того, используете выThreadPool
или нет.Пожалуйста, если вы обнаружите, что пишете код потоковой передачи в ASP.NET - подумайте, можно ли его переписать для использования уже существующих асинхронных методов, а если нет, то подумайте, действительно ли вам нужен код чтобы вообще работать в фоновом потоке. В большинстве случаев вы, вероятно, будете добавлять сложность без чистой выгоды.
источник
Action<T>
в качестве обратного вызова. Если вы имеете в виду, что выбор между использованием рабочего потока или потока ввода-вывода происходит на самом низком уровне, это сделано намеренно; только этот уровень может решить, нужен ли ему IOCP.ThreadPool
ограничивает вас таким образом, вероятно, потому, что они не доверяли разработчикам, чтобы сделать это правильно. Неуправляемый пул потоков Windows имеет очень похожий API, но на самом деле позволяет вам выбирать тип потока.Согласно Томасу Маркуадту из группы ASP.NET в Microsoft, безопасно использовать ASP.NET ThreadPool (QueueUserWorkItem).
Из статьи :
источник
Веб-сайты не должны порождать темы.
Обычно вы переносите эту функцию в службу Windows, с которой затем общаетесь (я использую MSMQ, чтобы общаться с ними).
-- Редактировать
Я описал реализацию здесь: Фоновая обработка на основе очередей в веб-приложении ASP.NET MVC
-- Редактировать
Чтобы объяснить, почему это даже лучше, чем просто потоки:
Используя MSMQ, вы можете общаться с другим сервером. Вы можете писать в очередь между машинами, поэтому, если вы по какой-то причине определите, что ваша фоновая задача слишком сильно использует ресурсы главного сервера, вы можете просто перенести ее довольно тривиально.
Это также позволяет вам выполнять пакетную обработку любой задачи, которую вы пытаетесь выполнить (отправлять электронные письма / что-то еще).
источник
Я определенно думаю, что общей практикой для быстрой асинхронной работы с низким приоритетом в ASP.NET было бы использование пула потоков .NET, особенно для сценариев с высоким трафиком, поскольку вы хотите, чтобы ваши ресурсы были ограничены.
Кроме того, реализация потоковой передачи скрыта - если вы начинаете порождать свои собственные потоки, вы также должны правильно управлять ими. Я не говорю, что вы не можете этого сделать, но зачем изобретать велосипед?
Если производительность становится проблемой, и вы можете установить, что пул потоков является ограничивающим фактором (а не соединения с базой данных, исходящие сетевые соединения, память, тайм-ауты страниц и т. Д.), Тогда вы настраиваете конфигурацию пула потоков, чтобы разрешить больше рабочих потоков, большее количество запросов в очереди. , и т.д.
Если у вас нет проблем с производительностью, то выбор создания новых потоков для уменьшения конкуренции с очередью запросов ASP.NET является классической преждевременной оптимизацией.
В идеале вам не нужно использовать отдельный поток для выполнения операции журналирования - просто позвольте исходному потоку завершить операцию как можно быстрее, и именно здесь MSMQ и отдельный поток / процесс потребителя входят в картину. Я согласен с тем, что это сложнее и требует больше усилий для реализации, но здесь вам действительно нужна надежность - непостоянство общей очереди в памяти быстро изнашивает ее.
источник
Вам следует использовать QueueUserWorkItem и избегать создания новых потоков, как если бы вы избежали чумы. Для наглядного изображения, объясняющего, почему вы не будете морить ASP.NET голодом, поскольку он использует тот же ThreadPool, представьте себе очень опытного жонглера, который двумя руками держит в полете полдюжины кеглей для боулинга, мечей или чего-то еще. Чтобы наглядно понять, почему создание собственных потоков - это плохо, представьте, что происходит в Сиэтле в час пик, когда интенсивно используемые въездные пандусы на шоссе позволяют транспортным средствам немедленно въезжать в движение вместо использования света и ограничения количества въездов до одного каждые несколько секунд. . Наконец, подробное объяснение см. По этой ссылке:
http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx
Спасибо, Томас
источник
Эта статья неверна. ASP.NET имеет собственный пул потоков, управляемые рабочие потоки для обслуживания запросов ASP.NET. Этот пул обычно состоит из нескольких сотен потоков и отделен от пула ThreadPool, который несколько меньше процессоров.
Использование ThreadPool в ASP.NET не будет мешать рабочим потокам ASP.NET. Использование ThreadPool - это нормально.
Также было бы приемлемо настроить один поток, который предназначен только для регистрации сообщений и использования шаблона производитель / потребитель для передачи сообщений журнала в этот поток. В этом случае, поскольку поток является долгоживущим, вы должны создать один новый поток для запуска регистрации.
Использование новой ветки для каждого сообщения определенно излишне.
Другой альтернативой, если вы говорите только о ведении журнала, является использование библиотеки вроде log4net. Он обрабатывает ведение журнала в отдельном потоке и заботится обо всех проблемах контекста, которые могут возникнуть в этом сценарии.
источник
Я бы сказал, что статья неправильная. Если у вас большой магазин .NET, вы можете безопасно использовать этот пул в нескольких приложениях и на нескольких веб-сайтах (используя отдельные пулы приложений), просто на основе одного оператора в документации ThreadPool :
источник
System.Threading.ThreadPool
.На прошлой неделе мне задали аналогичный вопрос на работе, и я дам вам тот же ответ. Почему вы используете многопоточные веб-приложения по запросу? Веб-сервер - это фантастическая система, сильно оптимизированная для своевременного предоставления множества запросов (например, многопоточность). Подумайте, что происходит, когда вы запрашиваете практически любую страницу в Интернете.
Вы приводите пример удаленного ведения журнала, но это должно быть проблемой для вашего регистратора. Для своевременного получения сообщений должен существовать асинхронный процесс. Сэм даже указывает, что ваш регистратор (log4net) уже должен это поддерживать.
Сэм также прав в том, что использование пула потоков в CLR не вызовет проблем с пулом потоков в IIS. Однако здесь следует обратить внимание на то, что вы не создаете потоки из процесса, вы создаете новые потоки из потоков пула потоков IIS. Есть разница, и различие важно.
Источник
источник
Я не согласен с указанной статьей (C # feeds.com). Создать новую ветку легко, но опасно. Оптимальное количество активных потоков для запуска на одном ядре на самом деле удивительно мало - менее 10. Слишком легко заставить машину тратить время на переключение потоков, если потоки создаются для второстепенных задач. Потоки - это ресурс, который ТРЕБУЕТ управления. Абстракция WorkItem предназначена для этого.
Здесь есть компромисс между уменьшением количества потоков, доступных для запросов, и созданием слишком большого количества потоков, чтобы любой из них мог эффективно обрабатывать. Это очень динамичная ситуация, но я думаю, что ею следует активно управлять (в данном случае пулом потоков), а не оставлять это процессору, чтобы он опережал создание потоков.
Наконец, в статье делается несколько довольно широких заявлений об опасностях использования ThreadPool, но для их поддержки действительно требуется что-то конкретное.
источник
Трудно получить окончательный ответ на вопрос, использует ли IIS один и тот же ThreadPool для обработки входящих запросов, и, похоже, изменился в версиях. Поэтому было бы неплохо не использовать чрезмерно потоки ThreadPool, чтобы в IIS было их много. С другой стороны, создание собственного потока для каждой мелкой задачи кажется плохой идеей. Предположительно, у вас есть какая-то блокировка в вашем журнале, поэтому только один поток может работать за раз, а остальные будут просто по очереди получать запланированные и незапланированные (не говоря уже о накладных расходах на порождение нового потока). По сути, вы сталкиваетесь с теми же проблемами, для предотвращения которых был разработан ThreadPool.
Похоже, что разумным компромиссом для вашего приложения было бы выделение одного потока ведения журнала, в который вы могли бы передавать сообщения. Вы должны быть осторожны, чтобы отправлять сообщения как можно быстрее, чтобы не замедлить работу вашего приложения.
источник
Вы можете использовать Parallel.For или Parallel.ForEach и определить предел возможных потоков, которые вы хотите выделить для бесперебойной работы и предотвращения истощения пула.
Однако при работе в фоновом режиме вам нужно будет использовать чистый стиль TPL ниже в веб-приложении ASP.Net.
источник