Использование Task или async/await в IHttpAsyncHandler

С самого начала написания приложений ASP.NET, когда я хотел добавить многопоточность, есть 3 простых способа выполнить многопоточность в моем приложении ASP.NET:

  • С использованием System.Threading.ThreadPool,
  • Использование собственного делегата и вызов его BeginInvoke метод.
  • Использование пользовательских потоков с помощью System.Threading.Thread учебный класс.

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

Затем я хотел использовать новую задачу или async/await, чтобы написать IHttpAsyncHandler, Один пример, который вы можете найти, - это то, что Дрю Марш объясняет здесь: /questions/46221363/ispolzovanie-ihttpasynchandler-dlya-asinhronnogo-vyizova-webservice/46221384#46221384

Я предполагаю, что использование Task или async/await по-прежнему потребляет поток из пула потоков ASP.NET, и я не хочу по очевидной причине.

Не могли бы вы сказать мне, если я могу использовать Task (async/await) в фоновом потоке, как с System.Threading.Thread класс а не из пула потоков?

Заранее спасибо за помощь.

Томас

6 ответов

Решение

Я искал информацию через Интернет в течение нескольких дней. Позвольте мне подвести итог тому, что я нашел до сих пор:

ASP.NET ThreadPool факты

  • Как сказал Андрес: Когда async / await не будет использовать дополнительный поток ThreadPool? Только в том случае, если вы используете методы BCL Async. который использует поток IOCP для выполнения связанной операции ввода-вывода.

  • Andres продолжает... Если вы пытаетесь выполнить асинхронный код синхронизации или ваш собственный библиотечный код, этот код, вероятно, будет использовать дополнительный поток ThreadPool, если вы явно не используете IOCP ThreadPool или свой собственный ThreadPool.

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

  • ASP.NET использует потоки из пула потоков общеязыковой среды выполнения (CLR) для обработки запросов. Пока в пуле потоков есть потоки, ASP.NET не имеет проблем с отправкой входящих запросов.

  • асинхронный delegates использовать темы из ThreadPool.

Когда вы должны начать думать о реализации асинхронного выполнения?

  • Когда ваше приложение выполняет относительно длительные операции ввода-вывода (запросы к базе данных, вызовы веб-служб и другие операции ввода-вывода)

  • Если вы хотите работать с вводом / выводом, вам следует использовать поток ввода / вывода (порт завершения ввода / вывода) и, в частности, вы должны использовать асинхронные обратные вызовы, поддерживаемые любым классом библиотеки, который вы используете. Их имена начинаются со слов Begin а также End,

  • Если запросы в вычислительном отношении дешевы в обработке, то параллелизм, вероятно, является излишними издержками.

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

Должен ли я создавать новые темы?

  • Избегайте создания новых тем, как если бы вы избегали чумы.

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

А тпл?

  • TPL может адаптироваться для использования доступных ресурсов в процессе. Если сервер уже загружен, TPL может использовать всего одного работника и продвигаться вперед. Если сервер в основном свободен, он может использовать столько рабочих, сколько может сэкономить ThreadPool.

  • Задачи используют потоки пула потоков для выполнения.

Рекомендации

Эта ситуация где 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 на основе кода. Но ИМО async код легче читать.

Если вы хотите узнать больше о async, У меня есть вступительное сообщение в моем блоге.

Сказать, что "0 (ноль) потоков будут обслуживать этот запрос" не совсем точно. Я думаю, что вы имеете в виду "из ASP.NET ThreadPool", и в общем случае это будет правильно.

Когда async/await не будет использовать дополнительный поток ThreadPool? Только в том случае, если вы используете асинхронные методы BCL (например, предоставляемые асинхронными расширениями WebClient), использующие поток IOCP для выполнения операции, связанной с вводом-выводом.

Если вы пытаетесь выполнить асинхронное выполнение некоторого кода синхронизации или кода вашей собственной библиотеки, этот код, вероятно, будет использовать дополнительный поток ThreadPool, если вы явно не используете IOCP ThreadPool или свой собственный ThreadPool.

Спасибо, Андрес.

У группы Parallel Extensions есть запись в блоге об использовании TPL с ASP.NET, в которой объясняется, как TPL и PLINQ используют ASP.NET ThreadPool. В публикации даже есть таблица решений, которая поможет вам выбрать правильный подход.

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

Методы Task и Parallel, с другой стороны, адаптируются к ресурсам процесса и могут использовать для обработки всего один поток.

Что касается Async CTP, то существует небольшая концептуальная разница между конструкцией async / await и непосредственным использованием задач. Компилятор использует некоторую магию для преобразования ожидаемых заданий и продолжений за кулисы. Большая разница в том, что ваш код НАМНОГО чище и проще в отладке.

Еще одна вещь, которую следует учитывать, это то, что 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 или Delegate 3) запустить дорогой код вычисления в другом потоке из своего пользовательского пула потоков (или IOCP threadPool).

Второй случай МОЖЕТ быть достаточным для вас в зависимости от того, как долго выполняется ваш "расчетный" процесс и сколько у вас нагрузки. Безопасный вариант №3, но намного дороже в кодировании / тестировании. Я также рекомендую всегда использовать.NET 4 для производственных систем, использующих асинхронный дизайн, потому что в.NET 3.5 есть некоторые жесткие ограничения.

Есть хорошая реализация HttpTaskAsyncHandler для.NET 4.0 в проекте SignalR. Вы можете проверить это: http://bit.ly/Jfy2s9

Другие вопросы по тегам