Использование задачи или async/wait в IHttpAsyncHandler
С самого начала написания приложений ASP.NET, когда я хотел добавить потоки, есть 3 простых способа выполнить потоки в моем приложении ASP.NET:
- Использование
System.Threading.ThreadPool
.
- Использование пользовательского делегата и вызов его метода
BeginInvoke
.
- Использование пользовательских потоков с помощью класса
System.Threading.Thread
.
Первые два метода предлагают быстрый способ отключить рабочие потоки для вашего приложения. Но, к сожалению, они повредили общую производительность вашего приложения, так как они потребляют потоки из того же пула, который используется ASP.NET для обработки HTTP-запросов.
Затем я хотел использовать новую задачу или async/wait для записи IHttpAsyncHandler
. Один пример, который вы можете найти, это то, что объясняет Дрю Марш: qaru.site/info/527549/...
Я предполагаю, что использование Task или async/await все еще потребляет поток из пула потоков ASP.NET, и я не хочу по очевидной причине.
Не могли бы вы рассказать мне , если я могу использовать Task (async/await) в фоновом потоке, например, с System.Threading.Thread
class , а не из пула потоков?
Заранее благодарим за помощь.
Томас
Ответы
Ответ 1
Я искал информацию через интернет в течение нескольких дней. Позвольте мне подвести итог тому, что я нашел до сих пор:
Факты ASPP ThreadPool
-
Как сказал Андрес: Когда async/await не будет потреблять дополнительный поток ThreadPool? Только в том случае, если вы используете методы BCL Async. который использует поток IOCP для выполнения операции привязки ввода-вывода.
-
Андрес продолжает... Если вы пытаетесь выполнить асинхронный запуск какого-либо кода синхронизации или вашего собственного кода библиотеки, этот код, вероятно, будет использовать дополнительный поток ThreadPool если вы явно не используете IOCP ThreadPool или свой собственный ThreadPool.
Но насколько я знаю, вы не можете выбрать то, что хотите использовать поток IOCP, и правильная реализация threadPool не стоит усилий. Я сомневаюсь, что кто-то делает лучший, который уже существует.
-
ASP.NET использует потоки из пула потоков CLR для обработки запросов. До тех пор, пока в пуле потоков есть потоки, ASP.NET не имеет проблем с отправкой входящих запросов.
-
Async delegates
использует потоки из ThreadPool.
Когда вы должны начать думать о реализации асинхронного выполнения?
-
Когда ваше приложение выполняет относительно длительные операции ввода-вывода (запросы базы данных, вызовы веб-служб и другие операции ввода-вывода)
-
Если вы хотите работать с I/O, то вы должны использовать поток ввода-вывода (порт завершения ввода-вывода), и в частности вы должны использовать асинхронные обратные вызовы, поддерживаемые любым классом библиотеки, который вы используете с помощью. Их имена начинаются со слов Begin
и End
.
-
Если запросы вычислительно дешевы для обработки, то parallelism, вероятно, является ненужным служебным.
-
Если скорость входящего запроса высока, добавление большего количества parallelism, вероятно, даст мало преимуществ и может фактически снизить производительность, так как скорость входящего трафика может быть достаточно высокой, чтобы поддерживать работу процессоров.
Должен ли я создавать новые темы?
-
Избегайте создания новых потоков, как если бы вы избежали чумы.
-
Если вы фактически ставите в очередь достаточно рабочих элементов, чтобы ASP.NET не обрабатывал дополнительные запросы, тогда вы должны голодать пул потоков! Если вы выполняете в буквальном смысле сотни операций с интенсивным процессором в одно и то же время, что бы это ни значило, чтобы иметь другой рабочий поток для обслуживания запроса ASP.NET, когда машина уже перегружена.
И TPL?
-
TPL может адаптироваться к использованию доступных ресурсов в процессе. Если сервер уже загружен, TPL может использовать всего один рабочий и продвигаться вперед. Если сервер в основном свободен, они могут расти, чтобы использовать столько рабочих, сколько может понадобиться ThreadPool.
-
Задачи используют потоки threadpool для выполнения.
Ссылки
Ответ 2
В этой ситуации Task
, async
и await
действительно сияют. Здесь тот же пример, реорганизованный для полного использования async
(он также использует некоторые вспомогательные классы из моей библиотеки AsyncEx, чтобы очистить код отображения):
// First, a base class that takes care of the Task -> IAsyncResult mapping.
// In .NET 4.5, you would use HttpTaskAsyncHandler instead.
public abstract class HttpAsyncHandlerBase : IHttpAsyncHandler
{
public abstract Task ProcessRequestAsync(HttpContext context);
IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
var task = ProcessRequestAsync(context);
return Nito.AsyncEx.AsyncFactory.ToBegin(task, cb, extraData);
}
void EndProcessRequest(IAsyncResult result)
{
Nito.AsyncEx.AsyncFactory.ToEnd(result);
}
void ProcessRequest(HttpContext context)
{
EndProcessRequest(BeginProcessRequest(context, null, null));
}
public virtual bool IsReusable
{
get { return true; }
}
}
// Now, our (async) Task implementation
public class MyAsyncHandler : HttpAsyncHandlerBase
{
public override async Task ProcessRequestAsync(HttpContext context)
{
using (var webClient = new WebClient())
{
var data = await webClient.DownloadDataTaskAsync("http://my resource");
context.Response.ContentType = "text/xml";
context.Response.OutputStream.Write(data, 0, data.Length);
}
}
}
(Как отмечено в коде,.NET 4.5 имеет HttpTaskAsyncHandler
, который похож на наш HttpAsyncHandlerBase
выше).
По-настоящему классная вещь о async
заключается в том, что она не принимает нити при выполнении фоновой операции:
- Поток запроса ASP.NET запускает запрос, и он начинает загрузку с помощью
WebClient
.
- Пока выполняется загрузка,
await
фактически возвращается из метода async
, оставляя поток запросов. Этот поток запросов возвращается обратно в пул потоков - оставляя 0 (нулевые) потоки, обслуживающие этот запрос.
- Когда загрузка завершена, метод
async
возобновляется в потоке запроса. Этот поток запросов кратко используется для написания фактического ответа.
Это оптимальное решение для потоковой обработки (поскольку для записи ответа требуется поток запроса).
Исходный пример также использует потоки оптимально - насколько касается потока, он совпадает с кодом на основе async
. Но IMO код async
легче читать.
Если вы хотите узнать больше о async
, у меня есть введение post в моем блоге.
Ответ 3
Говоря о том, что "0 (нулевые) потоки будут обслуживать этот запрос," неточно полностью.
Я думаю, вы имеете в виду "из ASP.NET ThreadPool", а в общем случае это будет правильно.
Когда async/await не будет потреблять дополнительный поток ThreadPool?
Только в том случае, если вы используете методы BCL Async (например, те, которые предоставляются расширениями async WebClient), которые используют поток IOCP для выполнения операции привязки ввода-вывода.
Если вы пытаетесь выполнить асинхронный запуск какого-либо кода синхронизации или собственного кода библиотеки, этот код, вероятно, будет использовать дополнительный поток ThreadPool, если вы явно не используете IOPP ThreadPool или собственный ThreadPool.
Спасибо,
Андрес.
Ответ 4
Команда Parallel Extensions сообщение в блоге об использовании TPL с ASP.NET, которая объясняет, как TPL и PLINQ используют ASP.NET ThreadPool. На почте даже есть диаграмма решений, которая поможет вам выбрать правильный подход.
Короче говоря, PLINQ использует один рабочий поток на ядро из threadpool для всего выполнения запроса, что может привести к проблемам, если у вас высокий трафик.
С другой стороны, методы Task и Parallel будут адаптироваться к ресурсам процесса и могут использовать только один поток для обработки.
Что касается Async CTP, существует небольшая концептуальная разница между конструкцией async/await и непосредственным использованием Tasks. Компилятор использует магию для преобразования ожиданий в "Задачи и продолжения" за кулисами. Большая разница в том, что ваш код намного БОЛЕЕ чище и проще отлаживать.
Ответ 5
Другое дело, что async/await и TPL (Task) - это не одно и то же.
Пожалуйста, прочитайте этот отличный пост http://blogs.msdn.com/b/ericlippert/archive/2010/11/04/asynchrony-in-c-5-0-part-four-it-s-not-magic.aspx, чтобы понять, почему async/await не означает "использование фонового потока".
Возвращаясь к нашей теме здесь, в вашем конкретном случае, когда вы хотите выполнить некоторые дорогостоящие вычисления внутри AsyncHandler, у вас есть три варианта:
1) оставить код внутри Asynchandler, поэтому дорогостоящий расчет будет использовать текущий поток из ThreadPool.
2) запустите дорогой код калькуляции в другом потоке ThreadPool, используя Task.Run или делегат
3) Запустите дорогостоящий код калькуляции в другом потоке из вашего собственного пула потоков (или IOCP threadPool).
Второй случай МОЖЕТ быть достаточным для вас в зависимости от того, как долго выполняется ваш "расчет" и сколько у вас нагрузки. Безопасный вариант №3, но намного дороже в кодировании/тестировании. Я также рекомендую всегда использовать .NET 4 для производственных систем, используя асинхронный дизайн, поскольку в .NET 3.5 есть некоторые жесткие ограничения.
Ответ 6
Там хорошая реализация HttpTaskAsyncHandler для .NET 4.0 в проекте SignalR. Вы можете захотеть сделать это: http://bit.ly/Jfy2s9