Важным понятием синхронизации потоков является понятие «критической секции» программы.
Критическая секция — это часть программы, результат выполнения которой может непредсказуемо меняться, если переменные, относящиеся к этой части программы, изменяются другими потоками в то время, когда выполнение этой части еще не завершено.
Критическая секция всегда определяется по отношению к определенным критическим данным, при несогласованном изменении которых могут возникнуть нежелательные эффекты. В предыдущем примере такими критическими данными являлись записи файла базы данных.
Во всех потоках, работающих с критическими данными, должна быть определена критическая секция.
Заметим, что в разных потоках критическая секция состоит в общем случае из разных последовательностей команд.
Чтобы исключить эффект гонок по отношению к критическим данным, необходимо обеспечить, чтобы в каждый момент времени в критической секции, связанной с этими данными, находился только один поток.
При этом неважно, находится этот поток в активном или в приостановленном состоянии. Этот прием называют взаимным исключением.
Операционная система использует разные способы реализации взаимного исключения.
Некоторые способы пригодны для взаимного исключения при вхождении в критическую секцию только потоков одного процесса, в то время как другие могут обеспечить взаимное исключение и для потоков разных процессов.
Самый простой и в то же время самый неэффективный способ обеспечения взаимного исключения состоит в том, что операционная система позволяет потоку запрещать любые прерывания на время его нахождения в критической секции.
Однако этот способ практически не применяется, так как опасно доверять управление системой пользовательскому потоку — он может надолго занять процессор, а при крахе потока в критической секции крах потерпит вся система, потому что прерывания никогда не будут разрешены.
Для синхронизации потоков одного процесса прикладной программист может использовать глобальные блокирующие переменные.
С этими переменными, к которым все потоки процесса имеют прямой доступ, программист работает, не обращаясь к системным вызовам ОС.
Каждому набору критических данных ставится в соответствие двоичная переменная, которой поток присваивает значение 0, когда он входит в критическую секцию, и значение 1, когда он ее покидает.
Рис.Реализация критических секций с использованием блокирующих переменных
Фрагмент алгоритма потока.
D - критические данные
F(D) - блокирующая переменная .
Перед входом в критическую секцию поток проверяет, не работает ли уже какой-нибудь поток с данными D.
Если переменная F(D) установлена в 0, то данные заняты и проверка циклически повторяется.
Если же данные свободны (F(D) = 1), то значение переменной F(D) устанавливается в 0 и поток входит в критическую секцию.