Подтвердить что ты не робот

Последствия и плюсы/минусы смывания потока в С++

Недавно я прочитал статью, в которой указано, что использование \n предпочтительнее использования std::endl, потому что endl также очищает поток.
Но когда я искал немного больше информации по теме, я нашел сайт, в котором говорилось:

Если вы находитесь в ситуации, когда вам нужно избегать буферизации, вы можете использовать std:: endl вместо '\n

Теперь вот мой вопрос: в какой ситуации предпочтительнее не писать в буфер? Потому что я только видел преимущества этой техники. Не безопаснее ли писать в буфер? Поскольку он меньше жесткого диска, он будет перезаписываться быстрее, чем данные, хранящиеся на HD (я не уверен, что это правда).

4b9b3361

Ответ 1

При буферизации у вас не будет гарантий, что данные будут немедленно получены до того, как произойдет флеш. В определенных обстоятельствах вы можете столкнуться с неправильным порядком вывода и/или потерей данных/отладочной информации, например.

int main() {

    std::cout << "This text is quite nice and might as well be buffered";
    raise(SIGSEGV); // Oh dear.. segmentation violation
    std::cout << std::endl;
}

Живой пример

Вывод:

bash: line 7: 22235 Segmentation fault      (core dumped) ./a.out

выше будет не печатать любой текст, поскольку буферизация предотвратила отображение правильного вывода.

Теперь, если вы просто добавили сброс std::endl в конец буфера, это то, что вы получаете

int main() {

    std::cout << "This text is quite nice and might as well be buffered" << std::endl;
    raise(SIGSEGV); // Oh dear.. segmentation violation
    std::cout << std::endl;
}

Пример Live

Вывод:

This text is quite nice and might as well be buffered
bash: line 7: 22444 Segmentation fault      (core dumped) ./a.out

На этот раз вывод будет видимым до завершения программы.

Последствия этого факта многообразны. Чисто умозрительное: если данные были связаны с журналом сервера, ваше приложение могло быть разбито до фактического ведения журнала.

Ответ 2

Это будет предпочтительнее в любой ситуации, когда вы хотите, чтобы результат фактически отображался точно, когда он должен был отображаться.

Простой пример:

#include <iostream>
int main() {
    std::cout << "Please enter your name: " << std::endl;
    std::string name;
    std::cin >> name;
    ...
}

При буферизации на экране не появится текст, прежде чем пользователь наберет свое имя, поэтому пользователь будет смущен. (Обратите внимание, что на самом деле может быть действительно трудно или невозможно получить этот пример с полной поддержкой буферизации, поскольку С++ может принять специальные меры для сброса std::cout перед любым входом из std::cin, см. Почему нам нужно связывать std:: cin и std:: cout? Но это всего лишь теоретический пример: если буферизация полностью включена, пользователь не увидит приглашение.)

Такая ситуация может возникать время от времени, хотя это может быть не очень часто. Рассмотрите возможность записи в трубу для взаимодействия с другим процессом. Или даже если ваша программа записывает в файл журнала, и вы лично время от времени заглядываете в файл журнала, чтобы посмотреть, как он работает - в случае буферизации вы обычно не увидите вывод, который был напечатан из программы, но все же остается в буфере.

Еще одна важная ситуация для учета - если ваша программа сильно сбой, содержимое буфера может вообще не заканчиваться на жестком диске. (Я ожидаю, что деструкторы потока сбросят буфер, но авария может быть настолько серьезной, что деструкторы вообще не будут вызваны.)

Ответ 3

Предпочтительнее сбросить буфер, если вы нуждаетесь цель вашего потока для получения данных до закрытия потока.

Реальный пример - это журнал приложений, написанный из потока, который всегда открывается... Вы можете посмотреть этот журнал, пока программа все еще работает.

Ответ 4

Во-первых, немного ревизионистской истории.

В старые времена, когда все использовали библиотеку stdio.h для ввода-вывода, текст, который просматривался в интерактивном режиме, обычно был строковым буфером (или даже небуферизованным), а текст, который не был полностью буферизирован. Таким образом, если вы выведете '\n' в поток, он будет "всегда" делать правильные вещи: линии, которые пользователи просматривают, получают покраснение и видимость сразу, а строки, сбрасываемые в файл, получают буферизацию для максимальной производительности.

К сожалению, на самом деле это не всегда правая вещь; среда выполнения не всегда может предсказать, как пользователи действительно хотят видеть вывод вашей программы. Одна общая ловушка перенаправляет STDOUT - люди привыкают к запуску своей программы в консоли и видят вывод (с его буферизацией по строке) в консоли, а затем по какой-либо причине (например, работа с длительным запуском) они решают перенаправить STDOUT в файл и сразу же удивлен тем, что выход больше не буферизирован.

Я видел недели суперкомпьютерного времени, потраченного по этой причине; выход был нередко достаточно, что буферизация помешала кому-либо узнать, как продвигается работа.

Библиотека С++ iostream, однако, была разработана, чтобы упростить работу с Right Thing. За исключением случаев, когда синхронизация с stdio, это не делает эту смешную "возможно, буферизированную, возможно, полностью буферизированную" вещь. Он всегда использует полную буферизацию (за исключением случаев, когда вы делаете небуферизованный материал, конечно), и если вы хотите, чтобы все покраснело в новой строке, вы делаете это явно.

Итак, если вы сбрасываете кучу форматированного текста в файл, который люди не будут просматривать до его завершения, вы пишете \n для своих разрывов строк.

Но если вы пишете текст, на который действительно захотите взглянуть, когда вы его пишете, вы используете std::endl для своих разрывов строк, и он сразу отображается. И если вы пишете сразу несколько строк, вы можете сделать лучше: используйте '\n' для промежуточных разрывов строк и std::endl для окончательного (или '\n' и std::flush). Хотя в этой настройке производительность обычно не имеет значения, поэтому обычно просто использовать std::endl для всех разрывов строк.

Ответ 5

Я надеюсь, что вы потеряли ссылку на этот сайт, который вы нашли. std::endl делает не избегать буферизации. Он сбрасывает все, что находится в буфере. Если вам нужно избегать буферизации, используйте setf(ios_base::unitbuf). Это устанавливает поток для очистки после каждой вставки. Это значение по умолчанию для std::clog. Причина этого заключается в том, что чем меньше материала удерживается в буфере, тем больше вероятность того, что критические данные будут записаны в поток при сбое программы.

Для интерактивных программ также важна промывка: если вы пишете приглашение на std::cout, это хорошая вещь, если это сообщение появляется на дисплее до того, как программа начнет ждать ввода. Это делается автоматически, если вы используете std::cout и std::cin, если вы не испортили настройки синхронизации.

Многие программисты используют std::endl как причудливый способ написания '\n', но это не так. Вам не нужно очищать выходной буфер каждый раз, когда вы что-то пишете на него. Пусть ОС и стандартная библиотека выполняют свою работу; они позаботятся о своевременной доставке продукции в соответствующее место. Простой std::cout << '\n'; - это все, что необходимо для ввода новой строки в вывод, и рано или поздно это будет отображаться на дисплее. Если вам нужно показать это сейчас, как правило, потому что вы все время написали весь вывод и не хотите, чтобы отображаемая информация была неполной, используйте std::endl после последней строки вывода.