пользователей: 30398
предметов: 12406
вопросов: 234839
Конспект-online
РЕГИСТРАЦИЯ ЭКСКУРСИЯ

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

В среде, позволяющей исполнять несколько потоков одновременно, очень важно синхронизировать их деятельность.

Общий принцип синхронизации заключается в следующем: синхронизируемый поток «засыпает» до тех пор, пока не наступит определенное событие. Это событие определяет  сам поток и сообщает его ОС. Таким образом, поток синхронизирует свое выполнение с наступлением особого события.

В ОС предусмотрен ряд способов для синхронизации выполнения потоков, которые будут рассмотрены ниже:

управление приоритетами потоков;

wait-функции;

критические секции;

специальные объекты ядра.

Можно рассматривать понятия «свободен-занят» по аналогии с обыкновенным флажком Когда объект свободен, флажок поднят, а когда он занят, флажок опущен.

Потоки спят, пока ожидаемые ими объекты заняты (флажок опущен). Как только объект освободился (флажок поднят), спящий поток замечает это, просыпается и возобновляет выполнение. 

Критическая секция (critical section) — это небольшой участок кода, требующий монопольного доступа к каким-то общим данным.

Она позволяет сделать так, чтобы единовременно только один поток получал доступ к определенному ресурсу.

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

Критическая секция создается функцией:

  VOID InitializeCriticalSection(PCRITICAL_SECTION pcs);

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

InitializeCriticalSection должна быть вызвана до того, как один из потоков обратится к EnterCriticalSection. В документации Platform SDK недвусмысленно сказано, что попытка воспользоваться неинициализированной критической секцией даст непредсказуемые результаты.

Если Вы знаете, что структура CRITICAL_SECTION больше не понадобится ни одному потоку, удалите ее, вызвав DeleteCriticalSection:

  VOID DeleteCriticalSection(PCRITICAL__SECTION pcs);

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

Участок кода, работающий с разделяемым ресурсом, предваряется вызовом:

  VOID EnterCriticalSection(PCRITICAL_SECTION pcs);

Первое, что делает EnterCriticalSection, — исследует значения элементов структуры CRITICAL_SECTION. Если ресурс занят, в них содержатся сведения о том, какой поток пользуется ресурсом. EnterCriticalSection выполняет следующие действия.

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

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

Если значения элементов структуры указывают на то, что ресурс занял другим потоком, EnterCriticalSection переводит вызывающий поток в режим ожидания. Поток, пребывая в ожидании, не тратит ни кванта процессорного времени! Система запоминает, что данный поток хочет получить доступ к ресурсу, и как только поток, занимавший этот ресурс, вызывает LeaveCriticalSection — вновь начинает выделять нашему потоку процессорное время. При этом она передает ему ресурс, автоматически обновляя элементы структуры CRITICAL_SECTION.

Даже если два потока на многопроцессорной машине одновременно вызовут EnterCriticalSection, функция все равно корректно справится со своей задачей: один поток получит ресурс, другой — перейдет в ожидание.

Поток, переведенный EnterCriticalSection в ожидание, может надолго лишиться доступа к процессору, а в плохо написанной программе — даже вообще не получить его. Когда именно так и происходит, говорят, что поток "голодает". 

Вместо EnterCriticalSection можно воспользоваться;

BOOL TryEnterCriticalSection(PCRITICAL_SECTIQN pcs);

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

TryEnterCriticalSection позволяет потоку быстро проверить, доступен ли ресурс, и если нет, заняться чем-нибудь другим.

Для каждого вызова функции TryEnterCriticalSection, где она возвращает TRUE, надо предусмотреть парный вызов LeaveCriticalSection.

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

      VOID LeaveCriticalSection(PCRITICAL_SECTION pcs);

Эта функция просматривает элементы структуры CRITICAL_SECTION и уменьшает счетчик числа захватов ресурса вызывающим потоком на 1. Если его значение больше 0, LeaveCriticalSection ничего не делает и просто возвращает управление.

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

Пример «правильной» программы:

 CRITICAL_SECTION FLock;

DWORD WINAPI StartThread1(CONST LPVOID lpParam)

 


26.12.2015; 23:13
хиты: 0
рейтинг:0
Точные науки
информатика
для добавления комментариев необходимо авторизироваться.
  Copyright © 2013-2024. All Rights Reserved. помощь