Использование ThreadPool.QueueUserWorkItem в ASP.NET в сценарии с высоким трафиком - PullRequest
109 голосов
/ 25 августа 2009

У меня всегда было впечатление, что использование ThreadPool для (скажем, некритических) краткосрочных фоновых задач считалось лучшей практикой, даже в ASP.NET, но потом я наткнулся на эту статью , что, по-видимому, говорит об обратном - аргумент в том, что вы должны оставить ThreadPool для обработки связанных с ASP.NET запросов.

Итак, вот как я выполнял небольшие асинхронные задачи:

ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))

И статья предлагает вместо этого явно создать поток, аналогично:

new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()

Преимущество первого метода в том, что он управляемый и ограниченный, но есть вероятность (если статья верна), что фоновые задачи затем соперничают за потоки с обработчиками запросов ASP.NET. Второй метод освобождает ThreadPool, но за счет того, что он неограничен и, следовательно, потенциально использует слишком много ресурсов.

Итак, мой вопрос: правильный ли совет в статье?

Если ваш сайт получал так много трафика, что ваш ThreadPool заполнялся, то лучше выходить за пределы диапазона, или если полный поток ThreadPool подразумевает, что вы все равно достигаете предела своих ресурсов, в в каком случае вы не должны пытаться создавать свои собственные темы?

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

Ответы [ 11 ]

103 голосов
/ 15 апреля 2010

Другие ответы здесь, кажется, опускают самый важный момент:

Если вы не пытаетесь распараллелить ресурсоемкую операцию, чтобы быстрее выполнять ее на узле с низкой нагрузкой, нет смысла использовать рабочий поток вообще.

Это касается как свободных потоков, созданных new Thread(...), так и рабочих потоков в ThreadPool, которые отвечают на QueueUserWorkItem запросов.

Да, это правда, вы можете голодать ThreadPool в процессе ASP.NET, ставя в очередь слишком много рабочих элементов. Это не позволит ASP.NET обрабатывать дальнейшие запросы. Информация в статье точна в этом отношении; тот же пул потоков, который используется для QueueUserWorkItem, также используется для обслуживания запросов.

Но если вы на самом деле ставите в очередь достаточно рабочих элементов, чтобы вызвать это голодание, то вы должны истощать пул потоков! Если вы выполняете буквально сотни ресурсоемких операций одновременно, что хорошего в том, чтобы иметь другой рабочий поток для обслуживания запроса ASP.NET, когда машина уже перегружена? Если вы столкнулись с такой ситуацией, вам нужно полностью изменить дизайн!

Большую часть времени я вижу или слышу о ненадлежащем использовании многопоточного кода в ASP.NET, он не предназначен для организации работы с интенсивной загрузкой ЦП. Это для работы в очереди ввода-вывода. И , если вы хотите выполнить работу ввода-вывода, вам следует использовать поток ввода-вывода (порт завершения ввода-вывода).

В частности, вы должны использовать асинхронные обратные вызовы, поддерживаемые любым классом библиотеки, который вы используете. Эти методы всегда очень четко обозначены; они начинаются со слов Begin и End. Как в Stream.BeginRead, Socket.BeginConnect, WebRequest.BeginGetResponse и т. Д.

Эти методы do используют ThreadPool, но они используют IOCP, которые не мешают запросам ASP.NET. Они представляют собой особый вид облегченной резьбы, которая может быть "разбужена" сигналом прерывания от системы ввода-вывода. И в приложении ASP.NET у вас обычно есть один поток ввода-вывода для каждого рабочего потока, поэтому каждый отдельный запрос может иметь одну асинхронную операцию в очереди. Это буквально сотни асинхронных операций без какого-либо значительного снижения производительности (при условии, что подсистема ввода-вывода может не отставать). Это намного больше, чем вам когда-либо понадобится.

Просто имейте в виду, что асинхронные делегаты не работают таким образом - они в конечном итоге будут использовать рабочий поток, как ThreadPool.QueueUserWorkItem. Это могут делать только встроенные асинхронные методы классов библиотеки .NET Framework. Вы можете сделать это самостоятельно, но это сложно, немного опасно и, вероятно, выходит за рамки этого обсуждения.

На мой взгляд, лучшим ответом на этот вопрос является Не используйте ThreadPool или фоновый Thread экземпляр в ASP.NET . Это совсем не похоже на раскручивание потока в приложении Windows Forms, где вы делаете это, чтобы пользовательский интерфейс реагировал и не заботился о его эффективности. В ASP.NET вас беспокоит пропускная способность , и все эти переключения контекста во всех этих рабочих потоках абсолютно убивают вашу пропускную способность независимо от того, используете вы ThreadPool или нет.

Пожалуйста, если вы обнаружите, что пишете многопоточный код в ASP.NET - подумайте, можно ли переписать его для использования уже существующих асинхронных методов, а если нет, то подумайте, действительно ли вы действительно нужен код для запуска в фоновом потоке вообще. В большинстве случаев вы, вероятно, будете добавлять сложность без какой-либо чистой выгоды.

45 голосов
/ 18 мая 2011

Согласно Томасу Маркуадту из команды ASP.NET в Microsoft, безопасно использовать ASP.NET ThreadPool (QueueUserWorkItem).

Из статьи :

В) Если мое приложение ASP.NET использует потоки CLR ThreadPool, я не буду голодать ASP.NET,который также использует CLR ThreadPool для выполнения запросов? Blockquote

A) [T] o Подводя итог, не беспокойтесь о нехватке ASP.NET потоков, и если вы считаете, что есть проблемаздесь, дайте мне знать, и мы позаботимся об этом.

В) Должен ли я создавать свои собственные темы (новая тема)?Разве это не будет лучше для ASP.NET, поскольку он использует CLR ThreadPool.

A) Пожалуйста, не делайте.Или иначе говоря, нет !!!Если вы действительно умны - намного умнее меня - тогда вы можете создавать свои собственные темы;в противном случае, даже не думай об этом.Вот несколько причин, по которым вам не следует часто создавать новые темы:

1) Это очень дорого по сравнению с QueueUserWorkItem ... Кстати, если вы можете написать лучшую ThreadPool, чемCLR, я призываю вас подать заявку на работу в Microsoft, потому что мы определенно ищем таких людей, как вы! .

4 голосов
/ 25 августа 2009

Я определенно думаю, что общей практикой быстрой асинхронной работы с низким приоритетом в ASP.NET будет использование пула потоков .NET, особенно для сценариев с высоким трафиком, когда вы хотите, чтобы ваши ресурсы были ограничены.

Кроме того, реализация потоков является скрытой - если вы начинаете создавать свои собственные потоки, вы должны также правильно управлять ими. Не сказать, что ты не мог этого сделать, но зачем изобретать это колесо?

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

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

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

4 голосов
/ 25 августа 2009

Веб-сайты не должны проходить порождающие темы.

Вы обычно перемещаете эту функцию в службу Windows, с которой затем общаетесь (я использую MSMQ для общения с ними). ​​

- Правка

Я описал реализацию здесь: Фоновая обработка на основе очереди в веб-приложении ASP.NET MVC

- Изменить

Чтобы объяснить, почему это даже лучше, чем просто потоки:

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

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

2 голосов
/ 15 апреля 2010

Вы должны использовать QueueUserWorkItem и избегать создания новых потоков, как если бы вы избежали чумы. Для наглядного изображения, объясняющего, почему вы не будете голодать ASP.NET, поскольку он использует тот же ThreadPool, представьте себе очень опытного жонглера, использующего две руки для удержания полдюжины кеглей для боулинга, мечей или чего-либо еще в полете. Для наглядного представления о том, почему создавать собственные темы плохо, представьте себе, что происходит в Сиэтле в час пик, когда интенсивно используемые съезды на шоссе позволяют транспортным средствам сразу же входить в движение, а не используют свет и ограничивают число въездов до одного каждые несколько секунд. , Наконец, для подробного объяснения, пожалуйста, перейдите по этой ссылке:

http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx

Спасибо, Thomas

1 голос
/ 15 октября 2009

На прошлой неделе мне задали аналогичный вопрос на работе, и я дам вам тот же ответ. Почему вы пользуетесь многопоточными веб-приложениями для каждого запроса? Веб-сервер - это фантастическая система, сильно оптимизированная для своевременного предоставления множества запросов (то есть многопоточности). Подумайте, что происходит, когда вы запрашиваете практически любую страницу в Интернете.

  1. Запрос сделан для некоторой страницы
  2. HTML возвращается
  3. Html сообщает клиенту о необходимости выполнения дальнейших запросов (js, css, images и т. Д.)
  4. Дополнительная информация возвращается

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

Сэм также прав в том, что использование пула потоков в CLR не вызовет проблем с пулом потоков в IIS. Однако здесь следует иметь в виду, что вы не порождаете потоки из процесса, вы порождаете новые потоки из потоков пула потоков IIS. Есть разница, и это важно.

Потоки против процесса

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

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

Источник

1 голос
/ 15 октября 2009

Я бы сказал, что статья неправильная.Если вы работаете в крупном магазине .NET, вы можете безопасно использовать пул для нескольких приложений и нескольких веб-сайтов (используя отдельные пулы приложений), просто основываясь на одном утверждении в документации ThreadPool :

Для каждого процесса существует один пул потоков. Размер пула потоков по умолчанию составляет 250 рабочих потоков на каждый доступный процессор и 1000 потоков завершения ввода-вывода.Количество потоков в пуле потоков можно изменить с помощью метода SetMaxThreads.Каждый поток использует размер стека по умолчанию и работает с приоритетом по умолчанию.

1 голос
/ 14 октября 2009

Эта статья не верна. ASP.NET имеет собственный пул потоков, управляемых рабочих потоков, для обслуживания запросов ASP.NET. Этот пул обычно состоит из нескольких сотен потоков и отделен от пула ThreadPool, который несколько кратнее процессоров.

Использование ThreadPool в ASP.NET не будет мешать рабочим потокам ASP.NET. Использование ThreadPool - это нормально.

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

Использование новой темы для каждого сообщения определенно излишне.

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

0 голосов
/ 31 марта 2019

Вы можете использовать Parallel.For или Parallel.ForEach и определить предел возможных потоков, которые вы хотите выделить для бесперебойной работы и предотвращения истощения пула.

Однако, работая в фоновом режиме, вам нужно будет использовать чистый стиль TPL ниже в веб-приложении ASP.Net.

var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;

ParallelOptions po = new ParallelOptions();
            po.CancellationToken = ts.Token;
            po.MaxDegreeOfParallelism = 6; //limit here

 Task.Factory.StartNew(()=>
                {                        
                  Parallel.ForEach(collectionList, po, (collectionItem) =>
                  {
                     //Code Here PostLog(logEvent);
                  }
                });
0 голосов
/ 06 марта 2010

Независимо от того, использует ли IIS один и тот же ThreadPool для обработки входящих запросов, трудно получить окончательный ответ, и, похоже, он также изменился в зависимости от версии. Поэтому было бы неплохо не использовать потоки ThreadPool чрезмерно, чтобы в IIS их было много. С другой стороны, порождение собственного потока для каждой маленькой задачи кажется плохой идеей. Предположительно, у вас есть какая-то блокировка в журнале, так что за один раз может выполняться только один поток, а остальные будут просто по очереди планироваться и не планироваться (не говоря уже о накладных расходах на создание нового потока). По сути, вы сталкиваетесь с точными проблемами, которых ThreadPool должен был избежать.

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

...