Использование задачи или 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