Отключить буферизацию на перенаправленном стандартном канале (Win32 API, C++)
Я порождаю процесс из Win32, используя CreateProcess
, установив hStdOutput
а также hStdError
свойства STARTUPINFO
к ручкам трубы, созданным с CreatePipe
, У меня есть два потока, которые читают каналы, ожидая, когда станут доступны данные (или процесс завершится, и в этот момент он проверяет, что перед завершением потока не осталось данных).
Когда данные становятся доступными, я записываю вывод в большое текстовое поле.
Что происходит, так это то, что выходные данные буферизируются, поэтому медленно работающий процесс просто получает куски данных, выбрасываемые в текстовое поле, а не "как это происходит".
Я не уверен, что это канал, который выполняет буферизацию, или что-то связанное с перенаправлением.
Есть ли способ установить канал как небуферизованный или запустить процесс таким образом, чтобы стандартный вывод был отправлен как можно скорее?
Я тестирую с помощью тестового приложения, которое печатает строки с интервалом в одну секунду
Here is line one
(waits one second)
Here is line two
(waits one second)
... etc
4 ответа
Буферизация, вероятно, выполняется во время выполнения C (printf и т. Д.), И вы ничего не можете с этим поделать (IIRC выполняет проверку isatty() для определения стратегии буферизации).
В моем случае буферизация была на выходе клиента (как писал @Anders), который использует обычный
printf
. Возможно, это также зависит от реализации среды выполнения C (Visual Studio 2019), возможно, среда выполнения обнаруживает «не консоль» и включает буферизацию.
Поэтому я отключил буферизацию с помощью этого вызова
setvbuf(stdout, (char*)NULL, _IONBF, 0);
в моем клиенте теперь я получаю вывод сразу в конвейере на сервере.
Для полноты: вот как я читаю канал на сервере
HANDLE in;
CreatePipe(&in, &startup.hStdOutput, &sa, 0); // Pipe for stdout of the child
...
char buffer[16384];
DWORD read, total;
while (PeekNamedPipe(in, NULL, 0, &read, &total, NULL))
{
if (total == 0)
{
if (WaitForSingleObject(info.hProcess, 0) == WAIT_OBJECT_0)
{
if (PeekNamedPipe(in, NULL, 0, &read, &total, NULL) && total == 0)
break;
continue;
}
Sleep(10);
continue;
}
if (total > sizeof(buffer))
total = sizeof(buffer);
ReadFile(in, buffer, total, &read, NULL);
...
}
Есть SetNamedPipeHandleState, но он управляет только буферизацией для удаленных каналов, а не когда оба конца находятся на одном компьютере.
Мне кажется, вы можете решить проблему, если вы установите hStdOutput
а также hStdError
из STARTUPINFO
не трубить ручки, созданные с CreatePipe
, но вместо этого вы создаете именованные каналы (с CallNamedPipe
функционировать так же, как вы использовали, если до того, как использовать SECURITY_ATTRIBUTES с bInheritHandle
знак равно TRUE
см. http://msdn.microsoft.com/en-us/library/aa365782.aspx), а затем откройте его по имени в отношении CreateFile
с помощью FILE_FLAG_WRITE_THROUGH
флаг. Как вы можете прочитать в MSDN ( http://msdn.microsoft.com/en-us/library/aa365592.aspx):
Канальный клиент может использовать CreateFile, чтобы включить перекрывающийся режим, указав FILE_FLAG_OVERLAPPED, или включить режим сквозной записи, указав FILE_FLAG_WRITE_THROUGH.
Так что просто откройте трубу в отношении CreateFile
с помощью FILE_FLAG_WRITE_THROUGH
пометить и установить ручку / ручки на hStdOutput
а также hStdError
из STARTUPINFO
,