Создание высокопроизводительного серверного приложения требует реализации эффективной модели многопоточности. Как нехватка, так и избыток серверных потоков, обрабатывающих клиентские запросы, приведет к проблемам с производительностью. Например, если сервер создает единственный поток для обработки всех запросов, клиенты будут «голодать», так как сервер будет обрабатывать по одному запросу единовременно. Конечно, единственный поток мог бы обрабатывать сразу несколько запросов, переключаясь с одной операции ввода-вывода на другую. Однако такая архитектура крайне сложна и не позволяет использовать преимущества многопроцессорных систем. Другая крайность — создание сервером огромного пула потоков, когда для обработки чуть ли не каждого клиентского запроса выделяется свой поток. Этот сценарий обычно ведет к перегрузке процессоров потоками: множество потоков пробуждается, выполняет обработку данных, блокируется в ожидании ввода-вывода, а после обработки запроса снова блокируется в ожидании нового запроса. Одно только наличие слишком большого количества потоков заставило бы планировщик чрезмерно часто переключать контекст, и в итоге он отобрал бы на себя немалую часть процессорного времени.
Задача сервера — свести к минимуму число переключений контекста, чтобы избежать излишнего блокирования потоков, и в то же время добиться максимального параллелизма в обработке за счет множества потоков. C этой точки зрения идеальна ситуация, при которой к каждому процессору подключен один активно обрабатывающий клиентские запросы поток, что позволяет обойтись без блокировки потоков, если на момент завершения обработки текущих запросов их ждут другие запросы. Однако такая оптимизация требует, чтобы у приложения была возможность активизировать другой поток, когда поток, обрабатывающий клиентский запрос, блокируется в ожидании ввода-вывода (например, для чтения файла в процессе обработки).
Приложения используют объект
Создавая порт завершения, приложение указывает максимальное число сопоставленных с портом потоков, которые могут быть активны. Как уже говорилось, в идеале на каждом процессоре должно быть по одному активному потоку. Windows использует это значение для контроля числа актив ных потоков приложения. Если число активных потоков, сопоставленных с портом, равно заданному максимальному значению, выполнение потока, ждущего на порте завершения, запрещено. По завершении обработки текущего запроса один из активных потоков проверяет, имеется ли в очереди порта другой пакет. Если да, он просто извлекает этот пакет из очереди и переходит к обработке соответствующих данных; контекст при этом не переключается.