Асинхронные страницы в платформе ASP.NET - где другие потоки и как она повторно прикреплена?

Извините за этот немой вопрос на Асинхронных операциях. Это - то, как я понимаю это.

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

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

1) Где эти другие потоки расположены? Есть ли другой пул потоков?

2) ЕСЛИ ASP.NET нравится создавать новые потоки в этом другом пуле потоков(?), почему бы не увеличивать число потоков в основном объединении рабочего - они все работают на той же машине так или иначе? Я не вижу преимущества перемещения того запроса к этому другому пулу потоков. Память/ЦП должна быть тем же правом?

3) Если основной поток руки прочь от запроса к этому другому потоку, почему запрос не становится разъединенным? Это волшебно руки прочь от запроса к другому рабочему потоку где-то в другом месте и когда длительный процесс завершается, он выбирает поток от основного пула рабочего и отправляет ответ клиенту. Я поражен..., но как это работает?

9
задан rkrauter 5 April 2010 в 01:41
поделиться

4 ответа

Вы не сказали, какую версию IIS или ASP.NET вы используете. Многие говорят о IIS и ASP.NET так, как будто это одно и то же, но на самом деле это два компонента, работающих вместе. Обратите внимание, что IIS 6 и 7 прослушивают порт завершения ввода-вывода, где они получают данные о завершении из HTTP.sys. Для этого используется пул потоков IIS, и он имеет максимальное количество потоков 256. Этот пул потоков спроектирован таким образом, что он плохо справляется с длительными задачами. Команда IIS рекомендует переключиться на другой поток, если вы собираетесь выполнять существенную работу, например, выполненную обработчиком «интегрированного режима» ASP.NET ISAPI и / или ASP.NET на IIS 7. В противном случае вы будете связывать вверх потоки IIS и не позволять IIS собирать завершенные данные из HTTP.sys. Скорее всего, вас это не волнует, потому что вы не пишете собственный код, то есть вы не пишете ISAPI или собственный обработчик для Конвейер IIS 7. Вероятно, вы просто используете ASP.NET, и в этом случае вас больше интересует его пул потоков и то, как он работает.

В блоге есть сообщение по адресу http://blogs.msdn.com/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and- 6-0.aspx , объясняющий, как ASP.NET использует потоки. Обратите внимание, что для ASP.NET v2.0 и v3.5 в IIS 7 вам следует увеличить MaxConcurrentRequestsPerCPU до 5000 - это ошибка, которая была установлена ​​на 12 по умолчанию на этих платформах. Новое значение по умолчанию для MaxConcurrentRequestsPerCPU в ASP.NET v4.0 на IIS 7 - 5000.

Чтобы ответить на три ваших вопроса:

1) Во-первых, небольшой учебник. Одновременно может выполняться только 1 поток на ЦП.Когда у вас больше этого, вы платите штраф - переключение контекста необходимо каждый раз, когда ЦП переключается на другой поток, а это дорого. Однако, если поток заблокирован в ожидании работы ... тогда имеет смысл переключиться на другой поток, который может выполняться сейчас.

Итак, если у меня есть поток, который выполняет много вычислительной работы и сильно использует ЦП, а это занимает много времени, следует ли мне переключиться на другой поток? Нет! Текущий поток эффективно использует ЦП, поэтому переключение повлечет только затраты на переключение контекста.

Итак, если у меня есть поток, который делает запрос HTTP или SOAP на другой сервер и занимает много времени, следует ли мне переключать потоки? Да! Вы можете выполнять HTTP- или SOAP-запрос асинхронно, так что после того, как «отправка» произошла, вы могли раскрутить текущий поток и не использовать какие-либо потоки до тех пор, пока не будет завершен ввод-вывод для «приема». Между «отправкой» и «получением» удаленный сервер занят, поэтому локально вам не нужно блокировать поток, а вместо этого используйте асинхронные API-интерфейсы, предоставляемые в .NET Framework, чтобы вы могли расслабиться и получать уведомление по завершении.

Итак, ваш вопрос №1 был: «Где находятся эти другие потоки? Есть ли еще один пул потоков?» Это зависит. Большая часть кода, выполняемого в .NET Framework, использует CLR ThreadPool, который состоит из двух типов потоков: рабочих потоков и потоков завершения ввода-вывода. А как насчет кода, который не использует CLR ThreadPool? Что ж, он может создавать свои собственные потоки, использовать собственный пул потоков или что угодно, потому что он имеет доступ к API-интерфейсам Win32, предоставляемым операционной системой.Основываясь на том, что мы обсуждали немного назад, на самом деле не имеет значения, откуда берется поток, а поток является потоком с точки зрения операционной системы и оборудования.

2) Во втором вопросе вы утверждаете: «Я не вижу преимущества перемещения этого запроса в этот другой пул потоков». Вы правы, полагая, что переключение НЕ дает никаких преимуществ, если вы не собираетесь компенсировать это дорогостоящее переключение контекста, которое вы только что выполнили для переключения. Вот почему я привел пример медленного HTTP- или SOAP-запроса к удаленному серверу в качестве примера веской причины для переключения. И, кстати, ASP.NET не создает никаких потоков. Он использует CLR ThreadPool, и потоки в этом пуле полностью управляются CLR. Они довольно хорошо определяют, когда вам нужно больше потоков. Например, поэтому ASP.NET может легко масштабироваться от одновременного выполнения 1 запроса до одновременного выполнения 300 запросов, ничего не делая. Входящие запросы отправляются в CLR ThreadPool через вызов QueueUserWorkItem, и CLR решает, когда вызывать WaitCallback (см. MSDN).

3) Третий вопрос: «Если основной поток передает запрос этому другому потоку, почему запрос не отключается?» Что ж, IIS получает информацию о завершении ввода-вывода из HTTP.sys, когда запрос изначально поступает на сервер. Затем IIS вызывает обработчик ASP.NET (или ISAPI). ASP.NET немедленно помещает запрос в пул потоков CLR и возвращает ожидающий статус в IIS. Этот статус ожидания сообщает IIS, что мы еще не закончили, но как только мы закончим, мы сообщим вам об этом. Теперь ASP.NET управляет сроком службы этого запроса. Когда поток CLR ThreadPool вызывает ASP.NET WaitCallback (см. MSDN), он может выполнить весь запрос в этом потоке, что является нормальным случаем.Или он может переключиться на один или несколько других потоков, если запрос является так называемым асинхронным, т.е. у него есть асинхронный модуль или обработчик. В любом случае существуют четко определенные способы завершения запроса, и когда это наконец произойдет, ASP.NET сообщит IIS, что мы закончили, и IIS отправит последние байты клиенту и закроет соединение, если Keep-Alive не используется.

С уважением, Томас

10
ответ дан 4 December 2019 в 10:03
поделиться

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

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

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

A Типичному веб-разработчику .NET не обязательно знать об этом. Конечно, если вы пишете реальный веб-сервер или какой-либо другой сетевой сервер, вам обязательно нужно будет узнать об этом, потому что они являются единственным способом обработки сотен входящих подключений, не создавая при этом сотни потоков для их обслуживания. Если вам интересно, есть учебник Managed I / O Completion Port (CodeProject).

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

ThreadPool.QueueUserWorkItem(s => DoSomeWork(s));

Это не использует порт завершения ввода / вывода. Всегда. Он отправляет работу в один из обычных рабочих потоков, управляемых пулом потоков. То же самое, если вы используете асинхронные обратные вызовы:

Func<int> asyncFunc;

IAsyncResult BeginOperation(object sender, EventArgs e, AsyncCallback cb,
    object state)
{
    asyncFunc = () => { Thread.Sleep(500); return 42; };
    return asyncFunc.BeginInvoke(cb, state);
}

void EndOperation(IAsyncResult ar)
{
    int result = asyncFunc.EndInvoke(ar);
    Console.WriteLine(result);
}

Опять же - то же самое. Внутри EndOperation вы работаете в потоке ThreadPool worker . Вы можете проверить это, вставив следующий код отладки:

void EndSimpleWait(IAsyncResult ar)
{
    int maxWorkers, maxIO, availableWorkers, availableIO;
    ThreadPool.GetMaxThreads(out maxWorkers, out maxIO);
    ThreadPool.GetAvailableThreads(out availableWorkers, out availableIO);
    int result = asyncFunc.EndInvoke(ar);
}

Вставьте туда точку останова, и вы увидите, что availableWorkers на единицу меньше, чем maxWorkers , а maxIO и availableIO одинаковы.

Но некоторые асинхронные операции в .NET являются «специальными». На самом деле это не имеет ничего общего с ASP.NET напрямую - они также будут использовать порты завершения ввода-вывода в приложениях Winforms или WPF. Примеры:

И так далее, это далеко не полный список. Практически почти каждый класс в .NET Framework, который предоставляет свои собственные методы BeginXYZ и EndXYZ и предположительно может выполнять любой ввод-вывод, вероятно, использует порты завершения ввода-вывода. Это сделано для того, чтобы вам, разработчику приложения, было проще, потому что потоки ввода-вывода довольно сложно реализовать в .NET самостоятельно.

Я предполагаю, что расширение.Разработчики .NET Framework намеренно решили усложнить публикацию операций ввода-вывода (по сравнению с рабочими потоками, где вы можете просто написать ThreadPool.QueueUserWorkItem ), потому что это сравнительно «опасно», если вы не знаете, как используйте их правильно; напротив, на самом деле довольно просто создать их в Windows API .

Как и раньше, вы можете проверить, что происходит, с помощью некоторого отладочного кода:

WebRequest request;

IAsyncResult BeginDownload(object sender, EventArgs e,
    AsyncCallback cb, object state)
{
    request = WebRequest.Create("http://www.example.com");
    return request.BeginGetResponse(cb, state);
}

void EndDownload(IAsyncResult ar)
{
    int maxWorkers, maxIO, availableWorkers, availableIO;
    ThreadPool.GetMaxThreads(out maxWorkers, out maxIO);
    ThreadPool.GetAvailableThreads(out availableWorkers, out availableIO);
    string html;
    using (WebResponse response = request.EndGetResponse(ar))
    {
        using (StreamReader reader = new
            StreamReader(response.GetResponseStream()))
        {
            html = reader.ReadToEnd();
        }
    }
}

Если вы пройдете через этот код, вы увидите, что статистика потоков отличается. availableWorkers будет соответствовать maxWorkers , но availableIO на единицу меньше, чем maxIO . Это потому, что вы работаете в потоке ввода-вывода. Вот почему вы не должны выполнять какие-либо дорогостоящие вычисления в асинхронных обратных вызовах - размещение интенсивной работы ЦП на порте завершения ввода-вывода неэффективно и, в общем, плохо.

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

Итак, подведем итог - есть только один ThreadPool , но в нем есть разные типы потоков, и если вы выполняете медленные операции ввода-вывода, то это много более эффективно использовать потоки ввода-вывода.Это не имеет ничего общего с процессором или памятью, все дело в вводе-выводе и дескрипторах файлов.


Что касается пункта 3, то на самом деле это не вопрос «почему не запрос отключается», скорее вопрос «почему это ?» Сокет не закрывается просто потому, что в данный момент нет потока, отправляющего или получающего от него данные, точно так же, как ваша входная дверь не закрывается автоматически, если рядом нет никого, кто мог бы приветствовать гостей. Операции клиента могут истечь , если сервер не отвечает им, и впоследствии может выбрать отключение от их конца, но это уже другая проблема.

9
ответ дан 4 December 2019 в 10:03
поделиться

Ответ также зависит от того, о какой версии IIS вы говорите. В более ранних версиях ASP.NET не использовал «потоки IIS». Это были потоки .NET ThreadPool. В IIS 7 конвейеры IIS и ASP.NET были объединены. Я не знаю, какие потоки сейчас использует ASP.NET.

Суть в том, что не создавайте свои собственные темы.

0
ответ дан 4 December 2019 в 10:03
поделиться

1) Потоки находятся в w3svc или другом процессе, в котором запущен механизм ASP.NET в вашей конкретной версии IIS.

2) Не понимаю, что вы здесь имеете в виду. Фактически вы можете контролировать количество потоков в пуле рабочих потоков. Эта статья довольно хороша: http://msdn.microsoft.com/en-us/library/ms998549.aspx

3) Я думаю, вы путаете запросы и соединения ... Если честно, я не знаю Понятия не имею, как работают внутренние компоненты IIS, но обычно в приложениях, которые обрабатывают несколько запросов одновременно, есть ОДИН главный поток прослушивания, который затем передает фактическую работу дочернему потоку (и больше ничего не делает). Исходный запрос не «отключается», потому что все это происходит на совершенно разных уровнях стека сетевых протоколов. Windows Server без проблем принимает несколько подключений через TCP-порт 80. Подумайте о том, как работает TCP / IP, и о том, что он отправляет несколько отдельных пакетов информации. Вы думаете о «соединении», как об одном шланге, идущем от патрубка A к патрубку B, но, конечно, это не так, как это работает на самом деле. Это больше похоже на ведро, которое просто собирает все, что в него пролилось.

Надеюсь, это поможет.

2
ответ дан 4 December 2019 в 10:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: