Создание порта завершения ввода-вывода
октября 12 2009 by admin in Обязательный материалМетод порта завершения ввода-вывода построен на предположении, что существует верхний предел числа одновременно работающих потоков, т. е. 500 одновременных клиентских запросов не могут приводить к созданию 500 работающих потоков. Каково же в таком случае допустимое значение работающих потоков? Немного подумав, вы можете прийти к выводу, что на машине с двумя процессорами наличие более двух потоков — по одному на каждый процессор — смысла не имеет. Как только появляются дополнительные потоки, процессор вынужден тратить время на переключения контекстов, что является потенциальным недостатком параллельной модели.
Другой порок параллельной модели — необходимость создания нового потока для каждого клиентского запроса. Создавать поток, конечно, дешевле, чем новый процесс с его собственным виртуальным адресным пространством, но все равно создание потока требует ресурсов. Производительность службы можно увеличить, если при ее инициализации создать пул потоков и эти потоки будут приостанавливаться в процессе работы службы. Порт завершения ввода-вывода как раз и спроектирован с применением пула потоков.
Порт завершения ввода-вывода — пожалуй, самый сложный объект ядра. Для создания порта вызывается функция CreateloCompletionPort-.
HANDLE CreateIoCompletionPort( HANDLE hfile, HANDLE hExistingCompPort, UL0NG_PTR CompKey, DWORD dwNumberOfConcurrentThreads);
Эта функция выполняет две задачи: создает порт завершения ввода-вывода и ставит ему в соответствие устройство. Она слишком сложна: думаю, Microsoft следовало разбить ее на дие отдельные. Работая с портами завершения ввода-вывода, я разделяю ее возможности: создаю пару маленьких функций, скрывающих обращение к CreateloCompletionPort. Первую — CreateNewCom-pletionPort — я реализую так:
HANDLE CreateNewCompletionPort(DWORD dwNumberOfConcurrentThreads) { return(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, dwNumberOfConcurrentThreads)); }
Она принимает единственный аргумент — dwNumberOJConcurrentTbreads — и обращается к функции Windows CreateloComplelionPort, предавая ей в качестве первых трех параметров постоянные значения, а в качестве последнего — dwNumberOfConcurrentThreads. Первые три параметра CreateloCompletion-Port используются, только когда эта функция устанавливает соответствие между устройством и портом завершения (вскоре я об этом расскажу подробно). Чтобы просто создать порт завершения, я передаю в качестве первых трех параметров функции CreateloCompletionPort значения INVALID_HANDLE_VALUE, NULL и 0 соответственно.
Параметр dwNumberOfConcurrentThreads указывает порту завершения максимальное число потоков, которые могут выполняться одновременно. Если этим параметром передать 0, используется значение по умолчанию, равное количеству процессоров. Обычно это именно то, что нужно, поскольку при этом не производится лишних переключений контекста. Вы можете захотеть увеличить это значение, если обработка клиентского запроса требует длительных вычислений, которые редко блокируются, но делать это крайне нежелательно.
CreateloCompletionPort — пожалуй, единственная функция Windows, которая создает объект ядра, но при этом не имеет параметра для передачи адреса структуры SECURITY_ATTRIBUTES, применяемой для защиты. Дело в том, что порты завершения предназначены для применения в рамках одного процесса. Это станет ясно, когда я расскажу, как использовать порты завершения.