Почему нет условий гонки в std: cout в Visual Studio 2019?

#c #multithreading #std #race-condition

#c #многопоточность #std #условие гонки

Вопрос:

Я хотел показать другу простой пример условий гонки на основе std: cout в качестве общего ресурса, как описано в этой статье.

Итак, я ввел следующий код в совершенно новое консольное приложение Visual Studio 2019:

 #include <iostream>
#include <string>
#include <thread>

using namespace std;

void CallHome(string message)
{
    cout << "Thread " << this_thread::get_id() << " says " << message << endl;
}

int main()
{
    thread t1(CallHome, "Hello from Jupiter");
    thread t2(CallHome, "Hello from Pluto");
    thread t3(CallHome, "Hello from Moon");
    CallHome("Hello from Main/Earth");
    thread t4(CallHome, "Hello from Uranus");
    thread t5(CallHome, "Hello from Neptune");
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();
    return 0;
}
  

Согласно статье и ее скриншоту для Visual Studio 2015, я ожидал, что выходной текст будет перепутан с чередующимися фрагментами из разных потоков.

Тем не менее, я неизменно получал следующий результат при каждом запуске моей программы:

 Thread 1472 says Hello from Main/Earth
Thread 15112 says Hello from Jupiter
Thread 9248 says Hello from Neptune
Thread 17876 says Hello from Pluto
Thread 18452 says Hello from Uranus
Thread 18088 says Hello from Moon
  

Что там происходит? Что изменилось в среде выполнения C в последнее время? Это что-то связанное с потоковой обработкой или std: cout, что теперь освобождает его от условий гонки?

Комментарии:

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

2. Добавьте std::this_thread::sleep_for(1ms); в хвост ur void, чтобы сделать его длиннее. Я использовал vs и тот же код с добавлением, а затем увидел условие гонки (чередование выходных данных).

3. rextester.com/live/DJAQ2285

4. Это не решает вопрос, но в этом вопросе задается вопрос о общем значении условия гонки. Стандарт C имеет более узкое определение условий гонки, и стандартные потоки должны быть реализованы таким образом, чтобы предотвращать условия гонки в этом более узком смысле. Поэтому будьте осторожны здесь; чередование, которое этот код стремится продемонстрировать, в некоторых случаях является условием гонки, но в формальном смысле, который определяет стандарт C , это не условие гонки.

5. @PeteBecker Да, в этом случае моя демонстрация предназначалась скорее для логического условия гонки высокого уровня при доступе к общему ресурсу (std: out), а не для технической потокобезопасности (когда вы рискуете получить нарушения доступа к памяти и сбои при использовании не потокобезопасного кода и сторонних ресурсов). Итак, я полагаю, std: out как таковой является потокобезопасным (без сбоев и т.д.), Но все же это может привести к логическим условиям гонки, в зависимости от ожиданий программиста.

Ответ №1:

Я изменил void CallHome(string message) на печать сообщений 100 раз за поток,

 void CallHome(string message)
{
    for (int i = 0; i < 100;   i)
        cout << "Thread " << this_thread::get_id() << " says " << message << endl;
}
  

и я получил следующие сообщения.

 Thread 10588 says Hello from Uranus
Thread 10588 says Hello from Uranus
Thread 10588 says Thread 8116 says Hello from Neptune
Hello from Uranus
Thread 10588 says Hello from Uranus
  

Условие гонки может быть найдено, когда многие потоки пытаются получить общие ресурсы. 5 попыток слишком мало для проверки.

Комментарии:

1. Спасибо, это сработало. Я думаю, что после этой статьи процессоры стали заметно более мощными, поэтому они выполняют потоки быстрее, и времени для возникновения условий гонки недостаточно.

2. @JustAMartin Рад слышать, что это помогает. Условия гонки очень трудно обнаружить. Они с трудом обнаруживаются, но очень важны.