(MSDN) Сделать поток "оповещаемым" постоянно, не блокируя поток - PullRequest
0 голосов
/ 05 июля 2018

Я использую асинхронные вызовы процессов (APC) для выполнения перекрывающихся запросов ввода-вывода с использованием ReadFileEx и WriteFileEx. Как определено в MSDN, эти подпрограммы завершения функции (APC) будут обрабатываться только в том случае, если поток, определяющий APC, находится в состоянии «оповещения».

Примеры из здесь и там оба используют функцию SleepEx (), чтобы поддерживать оповещение потока, выполняя SleepEx(INFINITE, TRUE), так что поток будет оставаться в состоянии оповещения бесконечно, пока все APC завершены, что означает, что поток приостанавливается до завершения всех APC. Разве это не нарушает причину, по которой мы используем перекрывающиеся операции ввода-вывода, ReadFileEx и WriteFileEx? Я думал, что вся идея состоит в том, чтобы сделать поток отзывчивым и отложить много времени в фоновом режиме. Пожалуйста, помогите мне объяснить идею.

Хотя в первой ссылке упоминается, что мы можем использовать SleepEx(0,TRUE) для немедленного возврата потока и одновременно обрабатывать APC, я до сих пор не знаю, что делать в период до возврата APC и как узнать когда они вернутся. Моя цель - просто реагировать на темы, но ничего не делать.

1 Ответ

0 голосов
/ 05 июля 2018

Не совсем понятно, о чем вы здесь спрашиваете, но я попробую.

То, сводит ли ожидание 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;
  }
}
...