Просто разделите их и проверьте их один за другим:
if (A == 'O' && B == 'O' && C == 'O' || A == 'X' && B == 'X' && C == 'X')
// etc
Это не определяется стандартом C - это зависит от Вашей реализации стандартной библиотеки C. На самом деле стандарт C даже не упоминает потоки вообще, так как определенные системы (например, встроенные системы) не имеют многопоточности.
В реализации GNU (glibc
), большая часть более высокого уровня функционирует в stdio, которые имеют дело с FILE*
, объекты ориентированы на многопотоковое исполнение. Те, которые не являются обычно, имеют unlocked
на их имена (например, getc_unlocked(3)
). Однако потокобезопасность на уровне на вызов функции: при совершении множественных вызовов к printf(3)
, например, каждый из тех вызовов, как гарантируют, произведет атомарно, но другие потоки могли бы распечатать вещи между вызовами к printf()
. Если Вы хотите удостовериться, что последовательность вызовов ввода-вывода производится атомарно, можно окружить их парой flockfile(3)/funlockfile(3)
вызовы для блокировки эти FILE
дескриптор. Обратите внимание, что эти функции повторно используемы, таким образом, можно безопасно звонить printf()
промежуточный их, и это не приведет к мертвой блокировке, даже думал printf()
, самой звонит [1 110].
вызовы ввода-вывода низкого уровня такой как [1 111] должны быть ориентированы на многопотоковое исполнение, но я не на 100% уверен в этом - write()
, превращает системный вызов в ядро для выполнения ввода-вывода. То, как точно это происходит, зависит, на каком ядре Вы используете. Это могло бы быть sysenter
инструкция, или int
(прерывание) инструкция относительно более старых систем. Однажды в ядре, это до ядра, чтобы удостовериться, что ввод-вывод ориентирован на многопотоковое исполнение. В тесте я просто сделал с Версией 8.11.1, write(2)
Ядра Darwin, кажется, ориентирован на многопотоковое исполнение.
Они оба ориентированы на многопотоковое исполнение до такой степени, что Ваше приложение не откажет, если несколько потоков назовут их на том же дескрипторе файла. Однако без некоторой блокировки прикладного уровня, независимо от того, что записано, мог быть чередован.
Это ориентировано на многопотоковое исполнение; printf должен быть повторно используем, и Вы не вызовете странности или повреждения в Вашей программе.
Вы не можете гарантировать, что Ваш вывод от одного потока не запустит половину пути через вывод от другого потока. Если Вы заботитесь об этом, необходимо разработать собственный заблокированный выходной код для предотвращения множественного доступа.
Назовете ли вы это "потокобезопасным", зависит от вашего определения потокобезопасности. POSIX требует, чтобы функции stdio
использовали блокировку, так что ваша программа не упадет, не испортит состояния объекта FILE
и т.д., если вы будете использовать printf
одновременно из нескольких потоков. Однако, все операции stdio
формально заданы в терминах повторяющихся вызовов fgetc
и fputc
, поэтому атомарность в более широком масштабе не гарантируется. То есть, если потоки 1 и 2 пытаются одновременно вывести "Hello\n"
и "Goodbye\n"
, нет гарантии, что на выходе будет либо "Hello\nGoodbye\n"
, либо "Goodbye\nHello\n"
. С таким же успехом это может быть "Привет\nГелолодбой\n"
. На практике, большинство реализаций будут получать одну блокировку для всего вызова записи более высокого уровня просто потому, что это более эффективно, но ваша программа не должна так считать. Могут быть угловые случаи, когда это не делается; например, реализация может полностью отказаться от блокировки на небуферизованных потоках.
Правка: Приведенный выше текст об атомарности неверен. POSIX гарантирует, что все операции stdio
атомарны, но эта гарантия скрыта в документации для flockfile
: http://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html
Все функции, ссылающиеся на объекты ( FILE *), должны вести себя так, как будто они используют flockfile() и funlockfile() для получения прав собственности на эти объекты ( FILE *).
Вы можете сами использовать функции flockfile
, ftrylockfile
и funlockfile
для достижения более крупных, чем один вызов функции, атомарных записей.