(MSDN) Сделать поток постоянно доступным для оповещения, не блокируя поток

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

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

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


person ctheadn8954    schedule 05.07.2018    source источник
comment
Это не единственный способ обработки асинхронного ввода-вывода. вы можете опросить состояние ввода-вывода с помощью GetOverLappedResult, и вы можете поиграть с портами завершения ввода-вывода, чтобы создать шаблон реактора/проактора   -  person David Haim    schedule 05.07.2018
comment
Что вы имеете в виду, говоря, что нить должна отвечать? Что делает нить? Как распределяются рабочие элементы?   -  person Peter Ruderman    schedule 05.07.2018
comment
если вы имеете в виду поток пользовательского интерфейса - вам нужно использовать MsgWaitForMultipleObjectsEx с MWMO_ALERTABLE. с этим вы можете обрабатывать сообщения Windows и apc   -  person RbMm    schedule 06.07.2018
comment
@DavidHaim привет, я хотел бы знать, что GetOverlappedResult может сделать в моем случае, который использует Read/WriteFileEx с подпрограммами завершения? спасибо   -  person ctheadn8954    schedule 07.07.2018
comment
@PeterRuderman Поток должен оставаться отзывчивым и обновлять индикатор выполнения при одновременном чтении и записи файлов.   -  person ctheadn8954    schedule 07.07.2018
comment
@DavidHaim, я проверю эту функцию, спасибо   -  person ctheadn8954    schedule 07.07.2018


Ответы (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 выполняла завершение ввода-вывода в выделенном для вас рабочем потоке. Или, что еще лучше, используйте библиотеку, ориентированную на задачи более высокого уровня, и избегайте этих низкоуровневых деталей.

EDIT – Выполнение ввода-вывода в потоке главного окна

Короткий ответ на эту конкретную проблему: не делайте этого. Запустите ввод-вывод в рабочем потоке и отправьте результаты обратно в основной поток, используя 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;
  }
}
person Peter Ruderman    schedule 05.07.2018
comment
Спасибо за ответ. В настоящее время я использую то же самое, что и вы, в то время как (work_to_do)... и это моя точка зрения, задающая вопрос. Когда мы помещаем цикл while в основной поток, это в равной степени замораживает основной поток, что нарушает мою цель выполнения перекрывающегося запроса ввода-вывода. - person ctheadn8954; 07.07.2018
comment
Я вижу, чего ты хочешь сейчас. Я обновлю свой ответ. - person Peter Ruderman; 14.08.2018