(MSDN) Сделать поток "оповещаемым" постоянно, не блокируя поток
Я использую асинхронные вызовы процессов (APC) для выполнения запросов ввода-вывода с перекрытием, используя ReadFileEx и WriteFileEx. Как определено в MSDN, эти подпрограммы завершения функции (APC) будут обрабатываться только в том случае, если поток, определяющий APC, находится в состоянии "оповещения".
Примеры здесь и там оба используют функцию SleepEx(), чтобы держать поток оповещаемым, выполняя SleepEx(INFINITE, TRUE)
, так что поток будет оставаться в состоянии оповещения бесконечно, пока все APC не будут завершены, что означает, что поток приостановлен, пока все APC не будут выполнены. Разве это не нарушает причину, по которой мы используем перекрывающиеся операции ввода-вывода, ReadFileEx и WriteFileEx? Я думал, что вся идея состоит в том, чтобы сделать поток отзывчивым и отложить много времени в фоновом режиме. Пожалуйста, помогите мне объяснить идею.
Хотя в первой ссылке упоминается, что мы можем использовать SleepEx(0,TRUE)
чтобы поток возвращался немедленно и мог обрабатывать APC одновременно, я до сих пор не знаю, что делать в период до возвращения APC и как узнать, когда они вернутся. Моя цель просто держать поток отзывчивым, но ничего не делать.
1 ответ
Не совсем понятно, о чем ты здесь спрашиваешь, но я попробую.
То, что ожидание APC сводит на нет преимущества асинхронного ввода-вывода, полностью зависит от вашего дизайна. Если вы написали что-то вроде:
WriteFileEx(..., &overlapped, CompleteWrite);
SleepEx(INFINITE, TRUE);
Тогда очевидно, что вы ничего не добились, кроме чрезмерного усложнения вашей программы. Вы могли бы также сделать синхронный ввод-вывод.
Но вы также можете написать что-то вроде:
while (work_to_do)
{
DoSomeWork();
WriteFileEx(results);
SleepEx(0, TRUE);
}
Теперь вы избежали простоев. Если WriteFileEx занимает много времени, поток может продолжить работу в промежуточный период.
Вы также можете создать поток, который обслуживает запросы. Что-то вроде:
DWORD ThreadProc(...)
{
while (!done)
{
SleepEx(INFINITE, TRUE);
}
}
Этот поток просто сидит и ждет, пока кто-нибудь отправит ему команду, используя QueueUserAPC
, Если такой поток выполняет ввод / вывод, он может захотеть сделать это асинхронно, чтобы позволить ему обрабатывать другие команды в ожидании завершения ввода / вывода. (С другой стороны, чтобы избежать проблем с повторным входом, он может не захотеть этого делать.)
Я надеюсь, что это дает некоторое представление о том, как эти функции были предназначены для использования. Но имейте в виду, что это очень старые технологии. Вы бы редко, если вообще когда-либо, использовали бы их в современной программе. Вместо этого используйте такие функции, как CreateThreadPoolIo
/StartThreadPoolIo
чтобы Windows запустила завершение ввода / вывода в выделенном для вас рабочем потоке. Или, что еще лучше, используйте высокоуровневую библиотеку, ориентированную на задачи, и избегайте этих низкоуровневых деталей.
РЕДАКТИРОВАТЬ - Выполнение ввода / вывода в главном окне потока
Краткий ответ на эту конкретную проблему: не делай этого. Запустите ваш ввод-вывод в рабочем потоке и отправьте результаты обратно в основной поток, используя PostMessage
или же SendMessage
, Если по какой-то причине вы не можете этого сделать, вы должны использовать измененный цикл обработки сообщений. Вместо GetMessage
использовать MsgWaitForMultipleObjects
, это позволит вам обрабатывать оконные сообщения в ожидании завершения ввода-вывода.
// error checking omitted
OVERLAPPED overlapped{};
ReadFile(file_handle, buffer, bytes_to_read, &bytes_read, &overlapped);
while (true)
{
DWORD wait_result = MsgWaitForMultipleObjects(
1,
&file_handle,
FALSE,
INFINITE,
QS_ALLINPUT);
if (wait_result = WAIT_OBJECT_0)
{
// I/O completed...
break;
}
}