многопоточность — поврежденный вывод с C ++, cin, cout, потоками и sync_with_stdio

Я пытаюсь сделать программу на C ++ для обработки большого количества пакетов самым быстрым способом. Все пакеты, поступающие из стандарта, должны быть прочитаны как можно быстрее, отправлены в один поток из пула для выполнения обработки, а затем обработаны в выходной поток, который запишет пакет в стандартный вывод.

Когда вы используете стандартный ввод и вывод в C ++, рекомендуется перед любым вводом или выводом вызывать станд :: ios_base :: sync_with_stdio (ложь) функция. В некоторых средах это обеспечивает значительное ускорение, хотя следует избегать использования стандартных функций 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 вызывает сброс потока, влияя на производительность в этом случае (и проблема не была решена, только сведена к минимуму).

Это единственная точка в программе, использующая std :: cout, но если я сделаю вызов станд :: ios_base :: sync_with_stdio (ложь) в начале программы я получаю заметное ускорение, но мой вывод всегда каким-то образом искажается:

Packet id = Packet id = 4
Packet id = 5
Packet id = 6
Packet id = 7
Packet id = 8
Packet id = 9
Packet id = 10

Так в чем же проблема? Разве C ++ не может выполнять многопоточность, используя быстрый стандартный ввод / вывод?

10

Решение

Я наконец нашел виновника. Если вы ищете в Интернете, многие сайты рекомендуют использовать вызов sync_with_stdio, но они не говорят о потоках.

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

Проблема в том, что внутренне входной поток std :: cin вызывал std :: cout для очистки своего буфера, но поскольку потоки не были синхронизированы с мьютексом или чем-то подобным, выходные данные были повреждены. Зачем мне синхронизировать буферы, если они делают разные вещи? Почему std :: cin возился с std :: cout?

В C ++ по умолчанию стандартные потоки cin, cerr и clog привязаны к cout. Что это значит? Это означает, что когда вы пытаетесь читать из cin, сначала это вызовет сброс к cout. Иногда это что-то полезное, как вы можете прочитать Вот.

Но в моем случае это вызывало серьезные проблемы, так как же развязать потоки? Это очень легко, используя метод связи:

std::ios_base::sync_with_stdio(false);

std::cin.tie(nullptr);
std::cerr.tie(nullptr);

Или, если ваш компилятор не поддерживает C ++ 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, он тоже быстрее 🙂

23

Другие решения

Других решений пока нет …