Cинхронизация Windows. Процессы, потоки и методы синхронизации




Федеральное агентство по образованию

ГОУ ВПО «СИБИРСКИЙ ГОСУДАРСТВЕННЫЙ  ТЕХНОЛОГИЧЕСКИЙ УНИВЕРСИТЕТ»

 

 

 

 

 

КАФЕДРА СИСТЕМОТЕХНИКИ

 

 

 

 

 

 

ОПЕРАЦИОННЫЕ  СИСТЕМЫ

 

 

 

 

 

 

 

КУРСОВАЯ РАБОТА

 

Пояснительная записка

(СТ.000000. 088. ПЗ)

 

 

 

 

 

 

 

 

 

 

Руководитель

 

_________________

(дата оценка роспись)

Выполнил

Студентка гр.23-6

Чемрова А.П.

_________________

(дата роспись)

ГОУ ВПО «СИБИРСКИЙ ГОСУДАРСТВЕННЫЙ  ТЕХНОЛОГИЧЕСКИЙ УНИВЕРСИТЕТ»


 

 

 

 

КАФЕДРА СИСТЕМОТЕХНИКИ

 

 

 

 

 

Утверждаю «__»___________________201__г.

Зав. кафедрой____________________________

__________________________________

 

 

 

 

ЗАДАНИЕ

НА КУРСОВУЮ РАБОТУ ПО ОПЕРАЦИОННЫМ СИСТЕМАМ

 

Студент: Чемрова Алёна Павловна

 

Факультет автоматизации и систем управления

Тема: Cинхронизация Windows. Процессы, потоки и методы синхронизации

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Задание выдано

Руководитель ____________________


РЕФЕРАТ

Пояснительная записка содержит 19 страниц. Использовались семь литературных источников.

Целью работы является изучение понятия синхронизации и ее методов в операционной системе Windows. Изучение понятий процесс и поток, выяснение необходимости и способов реализации синхронизации процессов.

 

СОДЕРЖАНИЕ

 

1 Введение ………………………………………………………..…………….…5

2 Процессы и потоки……………………………………………………………...6

3 Синхронизация потоков………………….…………………………………...8

3.1 необходимость синхронизации……………………………………….8

3.2 механизм синхронизации и ее объекты……………………..……...8

3.3 проблемы синхронизации……………………………………………10

3.4 работа с объектами синхронизации………………………………..11

4 Методы синхронизации……………………………….……........................12

4.1 методы прерываний и сигналов……..………………………….….12

4.2 метод семафоров ..…………………………………………………..13

4.3 метод блокировки …………………………………………………...14

4.4 метод гармонически взаимодействующих последовательных 
процессов…………………………………………….…………………………..14

5 Приложение…………………………………………………………………….16

6 Заключение……………………………………………………………………..18

7 Библиографический список …………………………………………………..19

 

1 Введение

В данной работе рассмотрен принцип организации синхронизации в операционной системе Windows: что такое процессы и потоки, как, и какими средствами синхронизовать их псевдопараллельное выполнение.

 

2 Процессы и потоки

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

Конечно же, один процессор  может одновременно исполнять лишь один поток инструкций. Поэтому, когда потоки команд исполняются действительно параллельно и независимо, «многопоточность» возможна только в многопроцессорной машине.

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

В зависимости от ситуации потоки могут находиться в трех состояниях:

1 состояние активности - поток может выполняться, когда ему выделено процессорное время;

2 состояние готовности - поток неактивен и ожидает выделения процессорного времени;

3 состояние блокировки - когда поток заблокирован, ему вообще не выделяется время. Обычно блокировка ставится на время ожидания какого-либо события. При возникновении этого события поток автоматически переводится из состояния блокировки в состояние готовности.

Основные объекты потока:

1 контекст

2 стек

Контекст потока (thread context) – это набор значений регистров процессора. Его можно получить функцией GetThreadContext (см. Приложение).

 

 

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

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

Процесс – это набор потоков, работающих в едином адресном пространстве. В этом пространстве размещаются код и данные приложения (обычно это один exe- и несколько dll-модулей). Именно единство этого пространства и делает обмен данными между потоками приложения предельно простым. Один и тот же виртуальный адрес в разных процессах соответствует разным физическим адресам. Поэтому по одному и тому же виртуальному адресу в разных процессах могут находиться совершенно разные данные, при этом модификация данных в одном процессе никак не отразится на данных в другом. В первую очередь это сделано для повышения надежности всей системы, чтобы ошибка в одном приложении не могла привести к порче данных, принадлежащих другому.

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

Иногда приоритет потока может изменяться динамически. Так  интерактивные потоки, имеющие обычно класс приоритета Normal, система обрабатывает несколько иначе и несколько повышает фактический приоритет таких потоков, когда процесс, их породивший, находится на переднем плане. Это сделано для того, чтобы приложение, с которым в данный момент работает пользователь, быстрее реагировало на его действия.

 

 

 

3 Синхронизация потоков

3.1 необходимость синхронизации

Итак, в Windows выполняются  не процессы, а потоки. При создании процесса автоматически создается  его основной поток. Этот поток в  процессе выполнения может создавать  новые потоки, которые, в свою очередь, тоже могут создавать потоки и т.д. Процессорное время распределяется именно между потоками, и получается, что каждый поток работает независимо.

Все потоки, принадлежащие  одному процессу, разделяют некоторые  общие ресурсы - такие, как адресное пространство оперативной памяти или открытые файлы. Эти ресурсы принадлежат всему процессу, а значит, и каж

 

 

 

 

 

 

 

 

 

 

 

 

дому его потоку. Следовательно, каждый поток может работать с  этими ресурсами без каких-либо ограничений.

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

 

3.2 Механизм синхронизации и ее объекты

Синхронизация – согласование скоростей паралельно выполняющихся процессов.

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

Объекты синхронизации:

1 взаимоисключение (mutex);

2 критическая секция (critical section);

3 событие (event) ;

4 семафор (semaphore).

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

Два (или более) процесса могут создать мьютекс с одним и тем же именем, вызвав функцию CreateMutex (см. Приложение). Первый процесс действительно создает мьютекс, а следующие - получают дескриптор уже существующего объекта. Это дает возможность нескольким процессам получить дескриптор одного и того же мьютекса, освобождая программиста от необходимости заботиться о том, кто в действительности создает мьютекс.

Критическая секция помогает программисту выделить участок кода, где поток получает доступ к разделяемому ресурсу, и предотвратить одновременное использование ресурса. Перед использованием ресурса поток входит в критическую секцию (вызывает функцию EnterCriticalSection (см. Приложение)). Если после этого какой-либо другой поток попытается войти в ту же самую критическую секцию, его выполнение приостановится, пока первый поток не покинет секцию с помощью вызова LeaveCriticalSection (см. Приложение). Похоже на взаимоисключение, но используется только для потоков одного процесса.

Существует также функция TryEnterCriticalSection (см. Приложение), которая проверяет, занята ли критическая секция в данный момент. С ее помощью поток в процессе ожидания доступа к ресурсу может не блокироваться, а выполнять какие-то полезные действия.

События используются для уведомления ожидающих потоков о наступлении какого-либо события. Различают два вида событий - с ручным и автоматическим сбросом. Ручной сброс осуществляется функцией ResetEvent (см. Приложение). События с ручным сбросом используются для уведомления сразу нескольких потоков. При использовании события с автосбросом уведомление получит и продолжит свое выполнение только один ожидающий поток, остальные будут ожидать дальше. Функция CreateEvent (см. Приложение) создает объект-событие, SetEvent (см. Приложение) - устанавливает событие в сигнальное состояние, ResetEvent сбрасывает событие. Функция PulseEvent (см. Приложение) устанавливает событие, а после возобновления ожидающих это событие потоков (всех при ручном сбросе и только одного при автоматическом), сбрасывает его. Если ожидающих потоков нет, PulseEvent просто сбрасывает событие.

Семафор - это фактически объект-взаимоисключение со счетчиком. Данный объект позволяет "захватить" себя определенному количеству потоков. После этого "захват" будет невозможен, пока один из ранее "захвативших" семафор потоков не освободит его. Семафоры применяются для ограничения количества потоков, одновременно работающих с ресурсом. Объекту при инициализации передается максимальное число потоков, после каждого "захвата" счетчик семафора уменьшается. Сигнальному состоянию соответствует значение счетчика больше нуля. Когда счетчик равен нулю, семафор считается не установленным (сброшенным).

Каждый из объектов может находиться в так называемом сигнальном состоянии. Для каждого типа объектов это состояние имеет различный смысл. Потоки могут проверять текущее состояние объекта и/или ждать изменения этого состояния и таким образом согласовывать свои действия. Что еще очень важно - гарантируется, что когда поток работает с объектами синхронизации (создает их, изменяет состояние) система не прервет его выполнения, пока он не завершит это действие. Таким образом, все конечные операции с объектами синхронизации являются атомарными (неделимыми), как бы выполняющимися за один такт. Важно понимать, что никакой реальной связи между объектами синхронизации и ресурсами нет. Они не смогут предотвратить нежелательный доступ к ресурсу, они лишь подсказывают потокам, когда можно работать с ресурсом, а когда нужно подождать. Можно провести грубую аналогию со светофорами - они показывают, когда можно ехать, но ведь в принципе водитель может и не обратить внимания на красный свет.

3.3 проблемы синхронизации

Первая состоит в вопросе - если одна задача производит данные, а вторая их потребляет, то как задача-потребитель узнает, что готова очередная порция данных? Или, что еще интереснее, как она узнает, что очередная порция данных еще не готова? Типичный случай такого взаимодействия - асинхронное чтение с диска, когда программа дает дисковому драйверу запрос: «читай с такого-то сектора в такой-то блок памяти», и продолжает заниматься своими делами.

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

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

3.4 работа с объектами синхронизации

Чтобы создать тот  или иной объект синхронизации, производится вызов специальной функции WinAPI типа Create (напр. CreateMutex). Этот вызов возвращает дескриптор объекта (HANDLE), который может использоваться всеми потоками, принадлежащими данному процессу. Есть возможность получить доступ к объекту синхронизации из другого процесса - либо унаследовав дескриптор этого объекта, либо, что предпочтительнее, воспользовавшись вызовом функции открытия объекта (Open...). После этого вызова процесс получит дескриптор, который в дальнейшем можно использовать для работы с объектом. Объекту, если только он не предназначен для использования внутри одного процесса, обязательно присваивается имя. Имена всех объектов должны быть различны (даже если они разного типа). Нельзя, например, создать событие и семафор с одним и тем же именем.

По имеющемуся дескриптору  объекта можно определить его  текущее состояние. Это делается с помощью т.н. ожидающих функций. Чаще всего используется функция WaitForSingleObject (см. Приложение). Эта функция принимает два параметра, первый из которых - дескриптор объекта, второй - время ожидания в мсек. Функция возвращает WAIT_OBJECT_0, если объект находится в сигнальном состоянии, WAIT_TIMEOUT - если истекло время ожидания, и WAIT_ABANDONED, если объект-взаимоисключение не был освобожден до того, как владеющий им поток завершился. Если время ожидания указано равным нулю, функция возвращает результат немедленно, в противном случае она ждет в течение указанного промежутка времени. В случае, если состояние объекта станет сигнальным до истечения этого времени, функция вернет WAIT_OBJECT_0, в противном случае функция вернет WAIT_TIMEOUT. Если в качестве времени указана символическая константа INFINITE, то функция будет ждать неограниченно долго, пока состояние объекта не станет сигнальным. Если необходимо узнавать о состоянии сразу нескольких объектов, следует воспользоваться функцией WaitForMultipleObjects (см. Приложение). Чтобы закончить работу с объектом и освободить дескриптор вызывается функция CloseHandle. Очень важен тот факт, что обращение к ожидающей функции блокирует текущий поток, т.е. пока поток находится в состоянии ожидания, ему не выделяется процессорного времени.

4 методы Синхронизации

Существуют следующие  методы синхронизации:

1 прерывания и сигналы;

2 семафоры;

3 блокировка участков  файлов;

4 гармонически взаимодействующие последовательные процессы.

4.1 метод прерываний и сигналов

Прерывания, вызванные внутренними событиями, часто называют исключениями (exceptions). Исключения возникают при делении на ноль, неопределенном коде команды, ошибках обращения к памяти и т.д.

Реализации прерываний и исключений у разных процессоров  немного отличаются. 

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

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

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

Кроме того, программу, работающую совместно с процедурами обработки прерываний, нельзя представить в виде детерминированного конечного автомата. Это усложняет анализ алгоритмов и доставило в свое время много волнений теоретикам программирования.

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

4.2 метод семафоров

Семафор Дийкстры представляет собой целочисленную переменную, с которой ассоциирована очередь ожидающих процессов. Пытаясь пройти через семафор, процесс пытается вычесть из значения переменной 1. Если значение переменной больше или равно 1, процесс проходит сквозь семафор успешно (семафор открыт). Если переменная равна нулю (семафор закрыт), процесс останавливается и ставится в очередь.

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

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

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

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

4.3 метод блокировки

Существует два типа блокировок: на чтение и на запись. Блокировка на чтение разрешает другим процессам читать из заблокированного участка и даже ставить туда такую же блокировку, но запрещает писать в этот участок и, тем более, блокировать его на запись. Этим достигается уверенность в том, что структуры данных, считываемые из захваченного участка, никем не модифицируются, поэтому гарантирована их целостность и непротиворечивость.

В свою очередь, блокировка на запись запрещает всем, кроме  блокирующего процесса, любой доступ к заблокированному участку файла. Это означает, что данный участок файла сейчас будет модифицироваться, и целостность данных в нем не гарантирована.

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

4.4 метод гармонически взаимодействующих последовательных процессов

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

Желание устранить эти проблемы привело в свое время Дийкстру к концепции, известной как гармонически взаимодействующие последовательные процессы. Эта концепция состоит в следующем:

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

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5 приложение

1 функция GetThreadContext:

BOOL GetThreadContext( 

 HANDLE hThread, 

 LPCONTEXT lpContext

);

 

2 функция CreateMutex:

HANDLE CreateMutex 

 LPSECURITY_ATTRIBUTES lpMutexAttributes,  
 BOOL bInitialOwner,    
 LPCTSTR lpName     
);

 

3 функция EnterCriticalSection:

VOID EnterCriticalSection

(

LPCRITICAL_SECTION lpCriticalSection

 

);

 

4 функция EnterCriticalSection:

VOID LeaveCriticalSection

(

LPCRITICAL_SECTION lpCriticalSection

 

);

 

5 функция TryEnterCriticalSection:

BOOL TryEnterCriticalSection(

  LPCRITICAL_SECTION lpCriticalSection 

);

 

6 функция ResetEvent:

BOOL ResetEvent 

 HANDLE hEvent   
);

 

7 функция CreateEvent:

HANDLE CreateEvent 

 LPSECURITY_ATTRIBUTES lpEventAttributes, 
 BOOL bManualReset, 
 BOOL bInitialState, 
 LPCTSTR lpName 
);

 

8 функция SetEvent:

BOOL SetEvent 

 HANDLE hEvent 
);

 

9 функция PulseEvent:

BOOL PulseEvent(HANDLE hEvent);

DWORD WaitForSingleObject(

  HANDLE hObject,

  DWORD  dwTimeout);

 

10 функция WaitForMultipleObjects:

DWORD WaitForMultipleObjects(

  DWORD cObjects, 

  CONST HANDLE *lphObjects,

  BOOL  fWaitAll,

  DWORD dwTimeout); 

 

Заключение

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

 

7 Библиографический список

1. http://www.sofmos.com/lyosha/Articles/multithreading1.html

2. http://www.rsdn.ru/article/baseserv/mt.xml#ESB

3. http://www.codenet.ru/progr/cpp/process-threads-sync.php

4. http://www.cyberguru.ru/cpp-sources/win32/funktsii-createevent-setevent-resetevent.html

5.http://cbuilder.inf.ua/index.php?option=com_content&task=view&id=102&Itemid=2

6. http://www.frolov-lib.ru/books/bsp/v26/ch4_1.htm

7. http://unixoid.spb.ru/OS/node4.html

 


Cинхронизация Windows. Процессы, потоки и методы синхронизации