Синхронизация потоков в операционной системе Windows, страница 2

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

Для подобных случаев в Windows используется объект, который называется событием (event). Событие не имеет ничего общего с системными сообщениями Windows. Говоря точнее, событие — это флаг общего доступа для нескольких потоков. В нашем примере дополнительные потоки будут ожидать до тех пор, пока основная программа не установит для них этот общий флаг. Существует несколько типов событий, каждый из которых предназначен для конкретной цели.

Чтобы сообщить основной программе о том, что дополнительный поток завершил анализ некоторого варианта, можно также использовать события. Однако вместо этого можно воспользоваться одним из вызов GetExitCodeThread, WaitForSingleObject или WaltForMultipleObjects. В нашем примере это  вполне приемлемое решение, так как в случае, если поток завершает вычисления, надобность в нем отпадает и он может прекратить свое существование.

Вариант 2

Предположим, что вы разрабатываете набор программ, каждая из которых вносит информацию об ошибках в общий файл журнала. Что случится, если одна из программ откроет файл, начнет запись, после чего Windows прервет ее исполнение и передаст управление другой программе? Предположим, что другая программа также попытается открыть файл и произвести запись в этот файл. Очевидно, что корректно завершить подобную операцию не удастся. Характер ошибки будет зависеть от того, в каком режиме первая программа открыла файл. Если файл был открыт для эксклюзивного доступа, вторая программа не сможет его открыть. Если файл был открыт для общего доступа, в процессе записи данные, вносимые в файл обеими программами, перемешаются, что вызовет путаницу.

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

1. Прочитать значение флага.

2. Если флаг установлен, перейти к шагу 1.

3. Установить флаг.

4. Открыть файл.

5. Записать в файл сообщение.

6. Закрыть файл.

7. Сбросить флаг.

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