Является ли cout синхронизированным / поточно-ориентированным? - PullRequest
107 голосов
/ 16 июня 2011

В общем, я предполагаю, что потоки не синхронизированы, это зависит от пользователя, чтобы сделать соответствующую блокировку.Однако такие вещи, как cout, получают специальную обработку в стандартной библиотеке?

То есть, если несколько потоков записывают в cout, могут ли они повредить объект cout?Я понимаю, что даже при синхронизации вы все равно получите случайное чередование, но гарантировано ли это чередование.То есть безопасно ли использовать cout из нескольких потоков?

Зависит ли этот поставщик?Что делает gcc?


Важно : Пожалуйста, предоставьте некоторую ссылку на ваш ответ, если вы скажете "да", так как мне нужны какие-то доказательства этого.

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

Ответы [ 4 ]

101 голосов
/ 16 июня 2011

Стандарт C ++ 03 ничего не говорит об этом. Если у вас нет никаких гарантий о поточной безопасности чего-либо, вы должны обращаться с этим как с поточным потоком.

Особый интерес здесь представляет тот факт, что cout буферизуется. Даже если вызовы write (или что-то еще, что обеспечивает этот эффект в этой конкретной реализации) гарантированно будут взаимоисключающими, буфер может совместно использоваться разными потоками. Это быстро приведет к повреждению внутреннего состояния потока.

И даже если доступ к буферу гарантированно является потокобезопасным, как вы думаете, что произойдет в этом коде?

// in one thread
cout << "The operation took " << result << " seconds.";

// in another thread
cout << "Hello world! Hello " << name << "!";

Вы, вероятно, хотите, чтобы каждая строка здесь действовала во взаимном исключении. Но как реализация может гарантировать это?

В C ++ 11 у нас есть некоторые гарантии. FDIS говорит следующее в §27.4.1 [iostream.objects.overview]:

Одновременный доступ к синхронизированным (§27.5.3.4) стандартным отформатированным и неформатированным функциям объекта ввода и ввода (§27.7.2.1) и выхода (§27.7.3.1) или стандартному потоку C несколькими потоками не должен приводить к в гонке данных (§1.10). [Примечание: пользователи по-прежнему должны синхронизировать одновременное использование этих объектов и потоков несколько потоков, если они хотят избежать чередования символов. - конец примечания]

Итак, вы не получите поврежденные потоки, но вам все равно нужно синхронизировать их вручную, если вы не хотите, чтобы вывод был мусором.

15 голосов
/ 16 июня 2011

Это отличный вопрос.

Во-первых, в C ++ 98 / C ++ 03 нет понятия «нить».Так что в этом мире вопрос не имеет смысла.

А как насчет C ++ 0x?См. ответ Мартиньо (который, я признаю, удивил меня).

Как насчет конкретных реализаций до C ++ 0x?Ну, например, вот исходный код для basic_streambuf<...>:sputc из GCC 4.5.2 (заголовок "streambuf"):

 int_type
 sputc(char_type __c)
 {
   int_type __ret;
   if (__builtin_expect(this->pptr() < this->epptr(), true)) {
       *this->pptr() = __c;
        this->pbump(1);
        __ret = traits_type::to_int_type(__c);
      }
    else
        __ret = this->overflow(traits_type::to_int_type(__c));
    return __ret;
 }

Очевидно, это не выполняет блокировку.И ни один не делает xsputn.И это определенно тот тип потокового буфера, который использует cout.

Насколько я могу судить, libstdc ++ не выполняет блокировку каких-либо операций потока.И я не ожидал бы, что это будет медленно.

Так что в этой реализации очевидно, что выход двух потоков может повредить друг друга ( не просто чередование).

Может ли этот код повредить саму структуру данных?Ответ зависит от возможных взаимодействий этих функций;например, что происходит, если один поток пытается очистить буфер, в то время как другой пытается вызвать xsputn или что-то еще.Это может зависеть от того, как ваш компилятор и процессор решат изменить порядок загрузки и хранения памяти;это займет тщательный анализ, чтобы быть уверенным.Это также зависит от того, что делает ваш ЦП, если два потока пытаются одновременно изменить одно и то же местоположение.

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

Резюме: "Я бы не стал".Создайте класс журналирования, который выполняет правильную блокировку, или перейдите на C ++ 0x.

В качестве слабой альтернативы вы можете установить для cout значение unbuffered.Вполне вероятно (хотя и не гарантировано), что пропустит всю логику, связанную с буфером, и вызовет write напрямую.Хотя это может быть слишком медленно.

7 голосов
/ 16 июня 2011

Стандарт C ++ не определяет, является ли запись в потоки потокобезопасной, но обычно это не так.

www.techrepublic.com / статьи / использование-СТЛ-потоки-для-легкого-C-плюс-плюс-поточно-каротажа

а также: Являются ли стандартные выходные потоки в C ++ поточно-ориентированными (cout, cerr, clog)?

UPDATE

Пожалуйста, ознакомьтесь с ответом @Martinho Fernandes, чтобы узнать, что об этом говорит новый стандарт C ++ 11.

6 голосов
/ 16 июня 2011

Как уже упоминалось в других ответах, это определенно зависит от поставщика, поскольку в стандарте C ++ не упоминается многопоточность (это изменяется в C ++ 0x).

GCC не дает много обещаний о безопасности потоков и ввода-вывода. Но документация для того, что он обещает, находится здесь:

ключевой материал, вероятно:

Тип __basic_file - это просто коллекция маленьких оберток вокруг слой C stdio (снова смотрите ссылку под структурой). Мы не делаем блокировки сами, но просто пройти к вызовы fopen, fwrite и т. д.

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

(Например, стандарт POSIX требует, чтобы C stdio FILE * операции являются атомными. POSIX-соответствующий C библиотеки (например, на Solaris и GNU / Linux) имеют внутренний мьютекс сериализовать операции над FILE * s. Тем не менее, вам все равно нужно не делать такие глупые вещи, как вызов fclose (fs) в одном потоке с последующим доступом фс в другом.)

Итак, если библиотека C вашей платформы потокобезопасный, тогда ваш fstream I / O операции будут потокобезопасными на самый низкий уровень. Для более высокого уровня операции, такие как манипулирование данные, содержащиеся в потоке классы форматирования (например, настройка обратные вызовы внутри std :: ofstream), вам нужно охранять такие доступы, как любой другой важный общий ресурс.

Я не знаю, изменилось ли что-нибудь, с упомянутым таймфреймом 3.0.

Документацию MSVC по безопасности потоков для iostreams можно найти здесь: http://msdn.microsoft.com/en-us/library/c9ceah3b.aspx:

Один объект является потокобезопасным для чтение из нескольких потоков. За Например, учитывая объект A, это безопасно читать A из потока 1 и из одновременно резьба 2.

Если один объект записывается в одним потоком, тогда все читает и пишет в этот объект на том же или другие темы должны быть защищены. За пример, заданный объект A, если поток 1 пишет в A, тогда поток 2 должен быть запрещено читать или писать А.

Можно читать и писать одному экземпляр типа, даже если другой поток читает или пишет в другой экземпляр того же типа. Например, данные объекты A и B того же типа, это безопасно, если А записывается в потоке 1 и B читается в теме 2.

...

iostream Classes

Классы iostream следуют тем же правила, как и другие классы, с одним исключение. Это безопасно писать на объект из нескольких потоков. За Например, поток 1 может написать в cout на то же время, что и поток 2. Однако это может привести к выходу из две темы перемешиваются.

Примечание: чтение из потокового буфера не считается операцией чтения. Это следует рассматривать как запись операция, потому что это меняет состояние класса.

Обратите внимание, что эта информация относится к самой последней версии MSVC (в настоящее время для VS 2010 / MSVC 10 / cl.exe 16.x). Вы можете выбрать информацию для более старых версий MSVC, используя раскрывающийся список на странице (и эта информация отличается для более старых версий).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...