Шаг 65.
Взаимодействие потоков

    На этом шаге мы приведем алгоритм взаимодействия потоков.

    Поговорим теперь о многопотоковой программе. В принципе, если не предполагается, что потоки как-то взаимодействуют друг с другом, технически не имеет значения, запущен один или несколько потоков. Сложности возникают, когда работа одного потока зависит от деятельности другого потока. Здесь возможны самые разные ситуации. Давайте рассмотрим все по порядку.

    Нам уже пришлось столкнуться с ситуацией, когда два параллельных процесса взаимодействовали друг с другом посредством глобальных переменных. Точнее, один процесс готовил данные для другого процесса. Здесь не было никакой сложности: просто один процесс с некоторой периодичностью менял содержимое переменной, а второй процесс, с другой периодичностью, читал из этой переменной. Если период обновления данных меньше периода получения данных, то мы почти с достоверностью (почти!) получаем, что второй процесс будет получать всегда свежие данные. Иногда соотношение между периодом обновления и периодом получения данных, как в нашем случае, вообще не имеет никакого значения.

    Часто случается, что данные невозможно получать периодически. Они могут, например, зависеть от деятельности третьего процесса. Как же второй процесс узнает, что данные уже готовы для передачи? На первый взгляд проблема решается введением дополнительной переменной, назовем ее FLAG. Примем, что при FLAG=0 данные не готовы, а при FLAG=1 данные готовы. Далее действует весьма простая схема.

NO_DAT:
    .   .   .   .   
    CMP  FLAG,1 
    JNE  NO_DAT
    .   .   .   .   
;Передача данных.
    .   .   .   .   
    MOV FLAG,0
    .   .   .   .   

    Это фрагмент, как вы понимаете, для второго потока. Первый же поток также должен проверять переменную FLAG и, если FLAG=0, поместить новые данные и установить значение переменной FLAG, равное единице. Данная схема совсем не плоха, например, когда один процесс ждет окончания работы другого процесса. Другими словами, данные являются результатом всей работы этого процесса. Например, запущен компилятор, а другой поток ждет окончания его работы, чтобы вывести результаты этой работы на некое устройство. Эта ситуация весьма распространена, но все же случай этот частный.

    А что будет, если процесс передачи данных должен производиться многократно? Легко видеть, что данная схема будет работать и в более сложном случае. Важно, однако, чтобы второй процесс менял содержимое FLAG только после того, как он возьмет данные, а первый процесс - после того, как положит данные. Если нарушить это правило, то может возникнуть коллизия, когда, например, второй процесс еще не взял данные, а они уже изменились.

    Такой подход можно осуществить и в более общем случае, когда два потока (или два процесса) должны поочередно получать доступ к одному ресурсу. Как легко видеть, данный подход предполагает, что ресурс открыт либо для одного, либо для другого процесса. Если поставить задачу несколько иным образом - процесс либо открыт, либо закрыт для доступа, то возникло бы некоторое затруднение. Действительно, вероятна такая ситуация, когда оба потока ожидают открытия ресурса. Другими словами, они непрерывно осуществляют проверку переменной FLAG (CMP FLAG, 1). Может статься, что они оба почти одновременно обратятся к ресурсу. Совершенно ясно, что здесь возникает необходимость в "третьей силе", которая бы занималась распределением доступа к ресурсу. Например, посылала бы сообщение вначале одному потоку и, если он ожидает доступа, давала доступ именно ему, а затем подобный процесс повторяется со вторым потоком.

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


    Замечание. В однозадачной операционной системе MS-DOS проблема совместного функционирования резидентных программ стояла весьма остро. Несмотря на то, что программисты, их писавшие, добивались весьма больших успехов, все же одновременная работа нескольких резидентных программ часто приводила к весьма заметным конфликтам.

    Следующие шаги будут всецело посвящены средствам синхронизации операционной системы Windows.

    На следующем шаге мы рассмотрим семафоры.




Предыдущий шаг Содержание Следующий шаг