Отключить буферизацию на перенаправленном стандартном канале (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,

Другие вопросы по тегам