Почему пакеты порта завершения ввода-вывода помещаются в очередь в порядке FIFO, если они могут быть исключены из очереди в другом порядке?

Документация Microsoft по Завершение ввода-вывода Порты указывают:

Обратите внимание, что хотя пакеты [completion] стоят в очереди в порядке FIFO, они могут быть исключены из очереди в другом порядке.

Насколько я понимаю, поток получает пакет завершения из порта завершения, вызывая GetQueuedCompletionStatus. Почему система ставит пакеты в очередь на порт завершения в порядке FIFO, если это не гарантирует получение пакетов в порядке FIFO?


person CRN    schedule 15.01.2015    source источник
comment
Я не понимаю, что означает "первым пришел - первым вышел" вышел, когда первый вышел не первым пришел....   -  person user541686    schedule 15.01.2015
comment
@Mehrdad Приятно знать, что я не единственный, кто озадачен.   -  person CRN    schedule 15.01.2015
comment
возможно это означает, что при простых обстоятельствах, используя один поток, пакеты, вероятно, FIFO, но разные условия потоков могут изменить порядок. т.е. поставленные в очередь в FIFO, должны были читаться в очереди в порядке вставки. но механизм удаления очереди различается по разным причинам   -  person slipperyseal    schedule 15.01.2015
comment
FIFO все еще что-то значит, даже если это не строгий FIFO. он говорит вам, что будет пропускная способность буфера. в отличие от стека LIFO, где пакеты могут застрять внизу.   -  person slipperyseal    schedule 15.01.2015


Ответы (2)


Утверждение, которое вы цитируете, предназначено для того, чтобы вы знали, что вам нужно выполнять собственную последовательность, если последовательность требуется между завершениями ввода-вывода в одном сокете. Это может понадобиться, если вы выполняете несколько вызовов WSARecv на одном сокете. Когда они завершатся, завершения перейдут в очередь IOCP в порядке FIFO, и это будет порядок, в котором были отправлены вызовы WSARecv.

Если вы продолжите читать этот документ, вы увидите этот фрагмент:

Потоки, которые блокируют свое выполнение на порте завершения ввода-вывода, высвобождаются в порядке «последним поступил — первым обслужен» (LIFO), и следующий пакет завершения извлекается из очереди FIFO порта завершения ввода-вывода для этого потока. Это означает, что когда пакет завершения передается потоку, система освобождает последний (самый последний) поток, связанный с этим портом, передавая ему информацию о завершении для самого старого завершения ввода-вывода.

Что показывает, что завершения удаляются из IOCP в порядке FIFO. Причина первого примечания заключается в том, что если у вас есть несколько потоков, ожидающих IOCP, то проблемы с планированием потоков могут означать, что ваш код обрабатывает завершения в порядке, отличном от порядка, в котором они были получены из IOCP.

Представьте, что у вас есть 2 потока, обслуживающих IOCP, и один сокет TCP с 3 ожидающими обработки WSARecv. Из сети поступает достаточно данных для завершения всех трех ожидающих выполнения WSARecv, и поэтому в IOCP вы получаете три завершения; мы назовем их A, B и C. Они расположены в том порядке, в котором были отправлены вызовы WSARecv, поэтому данные в буферах A, B и C должны обрабатываться для поддержания работоспособности потока TCP.

Первому из ваших потоков IOCP будет дано завершение A. Второму потоку будет дано завершение B. В зависимости от вашего оборудования (количество ядер и т. д.) и планировщика ОС либо поток 1, либо поток 2 могут быть запущены следующим, либо оба могут бежать одновременно. Это может вызвать у вас проблемы в описанной выше ситуации.

Я лично обхожу это, добавляя порядковый номер к каждому буферу при написании серверов, которые могут выдавать несколько WSARecv на одном сокете. Порядковый номер увеличивается, вставляется в буфер, а WSARecv выдается внутри той же блокировки, так что вся операция является атомарной. Когда происходит завершение, я гарантирую, что только один поток обрабатывает буферы для данного сокета (см. ">здесь), или я использую "упорядоченную коллекцию буферов", которая может гарантировать, что буферы обрабатываются в правильной последовательности (см. здесь).

Также обратите внимание, что для обеспечения корректности вам в любом случае необходимо заблокировать вызовы WSARecv (и WSASend) для данного сокета (см. html" rel="nofollow">здесь)

person Len Holgate    schedule 15.01.2015
comment
Спасибо, что поделились своим мнением, Лен. Ваше объяснение и пример заставили меня поверить, что IOCP на самом деле работают так, как я ожидал, до того, как я увидел, что пакеты [завершения] Microsoft ставятся в очередь в порядке FIFO [и] могут быть исключены из очереди в другом порядке. комментарий. Если я правильно понимаю, документация была бы точной, если бы в ней говорилось, что пакеты завершения ВСЕГДА ставятся в очередь и удаляются из очереди в/из порта завершения в порядке FIFO. Стандартное поведение потоков (планирование и т. д.) может повлиять на порядок, в котором потоки могут исключаться из очереди из порта завершения. - person CRN; 16.01.2015
comment
Это мое понимание этого, и это то, что показывают мои модульные тесты при запуске с IOCP, который обслуживается только одним потоком. - person Len Holgate; 16.01.2015

  1. во-первых, сделайте буфер достаточно большим для приема всех данных, если это возможно.
  2. НЕ отправляйте получение WSARecv несколько раз для одного сокета.
  3. после подключения или после чтения данных обработайте эту дату, а затем, наконец, опубликуйте WSARecv.

сумма: только один приемный буфер для одного сокета в любой момент времени.

Я заканчиваю свой сервер IOCP для серверной части, клиентской стороны, чтения файлов, записи, TCP и UDP только для ОДНОГО дескриптора iocp.

person Raymon SHan    schedule 15.01.2015
comment
Я делаю тест много лет назад. Установите размер полученного буфера в два раза больше размера окон ползунка TCP (максимум 64 КБ для TCPv4), чтобы получить максимальную производительность. - person Raymon SHan; 16.01.2015