2009-01-22 2 views

ответ

47

Это не указано стандартом C - это зависит от вашей реализации стандартной библиотеки C. Фактически, стандарт C даже не упоминает нити вообще, поскольку некоторые системы (например, встроенные системы) не имеют многопоточности.

В реализации GNU (glibc) большинство функций более высокого уровня в stdio, которые имеют дело с объектами FILE*, являются потокобезопасными. Те, которые обычно не имеют unlocked в своих именах (например, getc_unlocked(3)). Тем не менее, безопасность потока зависит от уровня вызова для каждой функции: если вы делаете несколько вызовов на printf(3), например, каждый из этих вызовов гарантированно выдаст атомарно, но другие потоки могут распечатывать информацию между вашими вызовами до printf(). Если вы хотите, чтобы последовательность вызовов ввода-вывода получалась атомарно, вы можете окружить их парами flockfile(3)/funlockfile(3) вызовов для блокировки дескриптора FILE. Обратите внимание, что эти функции являются реентерабельными, поэтому вы можете безопасно позвонить printf() между ними, и это не приведет к тупиковой ситуации даже мысли. printf() сам звонит flockfile().

Низкоуровневые вызовы ввода-вывода, такие как write(2), должны быть потокобезопасными, но я не уверен на 100%. write() делает системный вызов в ядре для выполнения ввода-вывода. Как именно это происходит, зависит от того, какое ядро ​​вы используете. Это может быть команда sysenter или инструкция int (прерывание) для старых систем. Как только внутри ядра, ядро ​​должно убедиться, что ввод-вывод является потокобезопасным. В тесте, которое я только что сделал с ядром Дарвина версии 8.11.1, write(2) представляется потокобезопасным.

+4

Многопоточность распространена во встроенных системы. – DanM

+22

Этот ответ игнорирует, что вопрос был помечен unix/linux. POSIX требует, чтобы stdio была потокобезопасной, что является весьма неудачным, так как оно убивает производительность, и поскольку нет практического способа работать с одним и тем же файлом из нескольких потоков (данные выйдут безнадежно чередующимися, атомарность будет только на уровне символов). –

+1

Иногда это вполне нормально, если выход чередуется, например. при регистрации через printf из нескольких потоков. – nob

4

Это поточно-безопасный; printf должен быть реентерабельным, и вы не будете вызывать какой-либо странности или коррупции в своей программе.

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

+0

Все вызовы printf, вероятно, будут использовать один и тот же буфер для построения строки. Многие реализации также используют буфер между scanf и printf, что может вызвать некоторые зависящие от ошибок отладки. –

+3

Я не знаю, что делают другие, но библиотека GNU C по умолчанию является потокобезопасной, поэтому нет, она не будет использовать тот же буфер. –

+1

Я не думаю, что 'printf' является реентерабельным, см. Http://stackoverflow.com/questions/3941271/why-are-malloc-and-printf-said-as-non-reentrant –

12

Они оба являются потокобезопасными до такой степени, что ваше приложение не будет разбиваться, если несколько потоков вызовут их в одном и том же дескрипторе файла. Однако без какой-либо блокировки на уровне приложений все, что написано, может чередоваться.

20

Можно ли назвать это «потокобезопасным», зависит от вашего определения потокобезопасности. POSIX требует stdio функций для блокировки, поэтому ваша программа не будет разбиваться, повреждать состояния объекта FILE и т. Д., Если вы используете printf одновременно из нескольких потоков. Тем не менее, все операции stdio формально указываются с точки зрения повторных вызовов fgetc и fputc, поэтому гарантируется более высокая степень автономности. То есть, если нитки 1 и 2 попытаются одновременно напечатать "Hello\n" и "Goodbye\n", то нет гарантии, что выход будет либо "Hello\nGoodbye\n", либо "Goodbye\nHello\n". Это может быть также "HGelolodboy\ne\n". На практике большинство реализаций приобретут единый замок для всего вызова на более высоком уровне, потому что он более эффективен, но ваша программа не должна этого допускать. Там могут быть угловые случаи, когда это не сделано; например, реализация, возможно, полностью исключает блокировку небуферизованных потоков.

Редактировать: Вышеупомянутый текст об атомарности неверен. POSIX гарантирует все stdio операции являются атомарными, но гарантия скрыта в документации flockfile: http://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html

Все функции, которые ссылаются (FILE *) объекты должны вести себя так, как будто они используют flockfile() и funlockfile() внутренне получить право собственности на эти объекты (FILE *).

Вы можете использовать flockfile, ftrylockfile и funlockfile функции самостоятельно, чтобы достичь большей, чем одну функции вызова атомной записи.

3

C получил новый стандарт, так как этот вопрос был задан (и последний ответ).

С11 теперь поставляется с поддержкой многопоточной и адреса многопоточном поведения потоков:

§7.21.2 Streams

¶7 Каждый поток имеет связанный с ним замок, который используется для предотвращать расы данных, когда несколько потоков выполнения доступа к потоку и до ограничивают чередование операций потока, выполняемых несколькими потоками. Только один поток может удерживать эту блокировку одновременно. Блокировка реентерабельная: один поток может удерживать блокировку несколько раз в заданное время.

¶8 Все функции, которые считывают, записывают, позиционируют или запрашивают местоположение потока, блокируют поток перед его доступом. Они освобождают блокировку, связанную с потоком, когда доступ завершен.

Таким образом, реализация с потоками C11 должна гарантировать, что использование printf является потокобезопасным.

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

Я наклоняюсь к нему, будучи гарантированным. В стандарте говорится о , ограничивающих чередование, поскольку некоторое перемежение, которое не изменяет результат, по-прежнему допускается; , например.fwrite несколько байт, fseek назад еще и fwrite до первоначального смещения, так что оба fwrite s являются обратными к спине. Реализация может свободно переупорядочить эти 2 fwrite s и объединить их в одну запись.


1: См Зачеркнутого текста R..'s answer для примера.

Смежные вопросы