Потоки. Синхронизирующие объекты Windows, страница 2

);

Замечание: для «чистоты эксперимента» возможно, стоит создавать потоки в «приостановленном» (suspended) состоянии, а затем (когда все сформированы) возобновить их выполнение (ResumeThread)

6.2. Управление приоритетами потоков

Поэкспериментируйте с приоритетами потоков.

В GUI-приложении проще и нагляднее для изменения приоритетов потоков ввести для каждого потока переключатели (достаточно трех radio buttons для каждого потока: нормальный, чуть повыше нормального, чуть повыше)

Функция для установки приоритета - SetThreadPriority()

7. Синхронизация потоков посредством критической секции

Основные понятия:

1)  сформировать структуру данных типа CRITICAL_SECTION (она может быть глобальной, локальной или динамической – главное, чтобы она существовала, пока ею кто-нибудь пользуется).

CRITICAL_SECTION cs; //поля этой структуры недокументированны. Пользоваться ею можно только посредством соответствующих Win32 API

2)  прежде чем синхронизировать потоки с помощью критической секции, нужно ее инициализировать вызовом функции:

InitializeCriticalSection(&cs);

3)  перед каждым (в обоих потоках) блоком, который модифицирует данные, вызвать

EnterCriticalSection(&cs);//блокирует данный поток, если эта критическая секция уже «занята» другим потоком. Это означает, что поток не может выполнить код, который «защищен» критической секцией => отправляется ОС в спячку.

Функция, анализируя поля структуры cs (некоторый счетчик ссылок), выясняет – вызвана ли она в первый раз (если счетчик нулевой). В этом случае функция увеличивает счетчик и разрешает выполнение потока дальше (то есть выполняется блок, модифицирующий данные). Допустим, в это время истекает квант времени, отпущенный данному потоку, или он вытесняется более приоритетным потоком, использующим те же данные: поток выполняется, пока не встречает функцию EnterCriticalSection(), функция выясняет, что объект cs уже «занят», она приостанавливает данный поток (он «засыпает»), а остаток процессорного времени система передает другому потоку.

4)  после выполнения этого блока вызвать

LeaveCriticalSection(&cs);

Эта функция уменьшает счетчик ссылок. Как только поток “освобождает” критическую секцию (счетчик ссылок становится 0), система “будит” ожидающий поток, снимая защиту от модификации данных.

5)  когда надобность в синхронизации потоков отпадает, следует вызвать

DeleteCriticalSection(&cs);

Эта функция освобождает все ресурсы, включенные в критическую секцию.

8. Синхронизация потоков посредством объекта – mutex

Основные понятия:

HANDLE CreateMutex(//если мьютекс создать не удалось - 0

LPSECURITY_ATTRIBUTES lpMutexAttributes,

                   BOOL bInitialOwner, //TRUE - позволяет вызывающему функцию потоку немедленно вступить во владение мьютексом при его создании (этот флаг игнорируется, если мьютекс уже существует) если TRUE, поток, создающий мьютекс, изначально имеет доступ к ресурсу, контролируемому мьютексом => мьютекс оказывается в занятом состоянии=> любой другой поток, ожидающий данный мьютекс, будет приостановлен, пока поток, создавший этот объект, не освободит его. Если параметр равен FALSE, это означает, что мьютекс не принадлежит ни одному из потоков => первый же поток из числа ожидающих этот объект может занять его и тем самым продолжить свое выполнение.

LPCTSTR lpName ); //имя объекта ядра или 0

Замечание: если Вы знаете, что существует мьютекс с данным именем, и Вы хотите сделать этот объект доступным другим процессам, нет нужды обращаться к CreateMutex(). Есть способ, получить описатель уже существующего мьютекса:

HANDLE OpenMutex( DWORD dwDesiredAccess, //стандартные (DELETE, READ_CONTROL, SYNCHRONIZE, WRITE_DAC, and WRITE_OWNER) + специфические для мьютекса - MUTEX_ALL_ACCESS и MUTEX_MODIFY_STATE

BOOL bInheritHandle,   //если TRUE, дочерний процесс //наследует возвращаемый описатель мьютекса

LPCTSTR lpName );  //указатель на строку и именем мьютекса

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

Когда требуется освободить объект –

BOOL ReleaseMutex(HANDLE hObject);//Замечание: если поток не владеет данным мьютексом - FALSE

Когда мьютекс Вам больше не нужен, вызовите функцию

BOOL CloseHandle(HANDLE hObject);     //где hObject – описатель мьютекса, возвращенный функцией CreateMutex()

8.1. Именованный мьютекс

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

9. Синхронизация потоков посредством объекта – semaphore

Основные понятия:

Объект-семафор создается функцией:

HANDLE CreateSemaphore(//если семафор создать не удалось, возвращаемое значение 0