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

Я хочу использовать пул потоков для инициирования/отмены перекрывающихся операций чтения, используя ReadFile() и CancelIo() соответственно, а также для обработки любых событий порта завершения после завершения операций чтения.

  1. Любой поток может инициировать операцию чтения
  2. Любой поток может обрабатывать событие завершения чтения
  3. Только поток, инициировавший чтение, может отменить его (это ограничение CancelIo()).

Я не уверен, как это реализовать. Обычно вызывается GetQueuedCompletionStatus() для ожидания завершения событий порта и WaitForSingleObject() для ожидания обычных событий, но неясно, как их смешивать. Если бы PostQueuedCompletionStatus() позволил мне указать конкретный поток для пробуждения, я был бы настроен. Любые идеи?

ОБНОВЛЕНИЕ: решение должно работать в Windows XP. К сожалению, это исключает использование CancelIoEx() или GetQueuedCompletionStatusEx().


person Gili    schedule 07.06.2009    source источник


Ответы (1)


1 и 2 легко, просто используйте порт завершения ввода-вывода.

Но, как вы обнаружили, для 3 требуется (до Windows V61) один и тот же поток.

При использовании Windows >= V6 GetQueuedCompletionStatusEx включает изменяемый параметр, который заставит его возвращать АПК выполняется на потоке. Поэтому используйте QueueUserAPC, чтобы поставить в очередь неактивный APC1, когда вы нужен этот конкретный поток для выполнения какой-либо другой работы. Вам, конечно, понадобится некоторая потокобезопасная очередь, чтобы предоставить прерванному потоку инструкции о том, что нужно отменить.

Если требуется совместимость с более ранними версиями, все становится сложнее. Возможности:

  • Используйте параметр времени ожидания GetQueuedCompletionStatus](http://msdn.microsoft.com/library/aa364986) регулярно возвращаться, чтобы проверять отмены.

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

Ни один из них не так хорош, но это всегда проблема старых версий: им не хватает функциональности.

1 Используйте APC без операций, а не выполняйте работу в APC, чтобы обойти ограничения на то, что можно делать в APC, и присущие ему проблемы с параллелизмом. (Поскольку APC выполняется в потоке, любые блокировки, удерживаемые потоком, удерживаются в APC, любое защищенное состояние будет произвольно несогласованным.)

person Richard    schedule 07.06.2009
comment
К сожалению, и GetQueuedCompletionStatusEx(), и CancelIoEx() требуют Windows Vista. Я обновлю вопрос, чтобы отметить, что я ищу решения, обратно совместимые с Windows XP. - person Gili; 07.06.2009
comment
Упс, забыл, что GQCSEx тоже ›=Vista... обновлялась. - person Richard; 07.06.2009
comment
Как вы уведомляете пул потоков инициирования и отмены о том, что он должен вызывать ReadFile()? Если я использую WaitOnMultipleObjects(), где одно событие обозначает ожидающий ReadFile(), не ограничиваю ли я все операции чтения одним потоком? - person Gili; 07.06.2009
comment
@Gill: я бы заставил каждый поток инициализации/отмены ожидать два события. Инструкция здесь и закрытие, событие инструкции будет указывать на то, что в этой очереди команд потока есть сообщение, ожидающее. Эта очередь команд используется другими потоками (например, пулом завершения) для отправки инструкций (инициация или отмена). Он не требует одного потока инициализации/отмены (хотя вам нужно будет выполнять много асинхронных операций, чтобы их было несколько), но также необходимо создать балансировку нагрузки нескольких потоков (инструкции инициализации может выполнять любой, но отменить нужно конкретно). - person Richard; 08.06.2009
comment
Отличный ответ, спасибо! В качестве примечания я только что узнал, что мне не нужно использовать порты завершения, потому что мой обработчик ввода-вывода тратит всего 0,3 миллисекунды (это не опечатка) на запрос чтения/записи. Я собираюсь упростить это, используя один поток :) - person Gili; 08.06.2009