Поврежденный вывод с С++, cin, cout, threads и sync_with_stdio
Я пытаюсь сделать программу на С++ для обработки множества пакетов самым быстрым способом. Все пакеты из стандарта должны быть прочитаны как можно быстрее, отправлены в один поток из пула, чтобы выполнить обработку, а затем обработаны выходным потоком, который будет записывать пакет в стандартный вывод.
Когда вы используете стандартный ввод и вывод в С++, он рекомендовал, чтобы перед любым вводом или выводом вы вызывали std:: ios_base:: sync_with_stdio (false) функция. В некоторых средах это обеспечивает большую скорость, хотя вам следует избегать использования стандартных функций C для ввода/вывода после вызова.
Ну, похоже, это отлично работает в одном потоке. Но, как я уже сказал, мое намерение использует один поток для ввода, один для вывода и несколько потоков для параллельной обработки. Я видел некоторые проблемы с выходом. Это выходной поток (очень упрощенный):
void PacketDispatcher::thread_process_output(OutputQueue& output_queue) {
std::vector<Packet> packet_list;
while(output_queue.get(packet_list)) {
for (const auto& packet: packet_list) {
std::cout << "Packet id = " << packet.id << "\n";
}
}
std::cout.flush();
}
Если я использовал std:: endl вместо "\n", было меньше коррупции, но std:: endl заставляет поток потока, влияя на производительность в этом случае (и проблема не была решена, только сведена к минимуму).
Это единственная точка в программе, использующая std:: cout, но если я сделаю вызов std:: ios_base:: sync_with_stdio (false) в начале из программы я получаю заметное ускорение, но мой результат поврежден всегда в некотором роде:
Packet id = Packet id = 4
Packet id = 5
Packet id = 6
Packet id = 7
Packet id = 8
Packet id = 9
Packet id = 10
Итак, где проблема? Разве С++ не способен выполнять многопоточность с использованием быстрого стандартного ввода/вывода?
Ответы
Ответ 1
Наконец я нашел преступника. Если вы ищете Интернет, многие сайты рекомендуют использовать вызов sync_with_stdio, но они не говорят о потоках.
Другие сайты говорят о iostreams и threads, например этот, но это не объясняет, почему я получал поврежденный вывод, когда я использовал std:: cin в только один поток и std:: cout в своем собственном потоке.
Проблема заключается в том, что внутренний поток входных данных std:: cin вызывал std:: cout, чтобы очистить его буфер, но в качестве потоков, не синхронизированных с мьютексом или чем-то похожим, результат был поврежден. Почему я должен синхронизировать буферы, если они делают разные вещи? Почему std:: cin возился с std:: cout?
В С++ по умолчанию стандартные потоки cin, cerr и clog привязаны к cout. Что это значит? Это означает, что, когда вы пытаетесь прочитать из cin, сначала это заставит поток отключиться. Иногда это полезно, поскольку вы можете читать здесь.
Но в моем случае это вызывало некоторые серьезные проблемы, поэтому, как развязать потоки?. Это очень просто, используя метод привязки:
std::ios_base::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cerr.tie(nullptr);
Или, если ваш компилятор не поддерживает С++ 11:
std::ios_base::sync_with_stdio(false);
std::cin.tie(static_cast<ostream*>(0));
std::cerr.tie(static_cast<ostream*>(0));
С этим изменяется мой вывод, теперь он исправляется:
Packet id = 1
Packet id = 2
Packet id = 3
Packet id = 4
Packet id = 5
Packet id = 6
Packet id = 7
Packet id = 8
Packet id = 9
Packet id = 10
И поскольку он избегает делать флеш каждый раз, когда используется std:: cin, он быстрее: -)