Поврежденный вывод с С++, 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, он быстрее: -)