Реферат: Синхронизация потоков

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

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

Можно выделить несколько случаев эффективного применения критических секций:

· читатели не конфликтуют с писателями (защищать надо только писателей);

· все потоки имеют примерно равные права доступа (скажем, нельзя выделить чистых писателей и читателей);

· при построении составных синхронизирующих объектов, состоящих из нескольких стандартных, для защиты последовательных операций над составным объектом.

Объекты исключительного владения

Объекты исключительного владения — mutex (mut ualex clusion) — могут принадлежать только одному потоку одновременно. Соответственно определяются операции над этими объектами:

· затребовать владение объектом;
При запросе владения система проверяет, владеет–ли какой–либо другой поток этим объектом или нет. Если имеется другой поток–владелец, то данный поток останавливается до тех пор, пока объект не освободиться. Как только объект становится свободным, система отдает его во владение новому потоку. Поток, уже владеющий объектом, может многократно вступать во владение им.

· освободить объект;
При освобождении объекта система просматривает, имеются–ли другие потоки, ожидающие освобождения этого объекта. Если имеются, то возобновит работу только один поток, а все остальные продолжат ожидание — объект mutex может быть во владении только у одного потока. Освобождать объект может только тот поток, который им в данный момент владеет, другие потоки этого сделать не могут. Для полного освобождения объекта поток должен столько раз освободить его, сколько раз он затребовал владение с того момента, как ему дали этот объект во владение.

Если учесть, что владеть объектом mutex может только один поток, то получается, что такие объекты похожи на критические секции — с того момента, как система отдала объект во владение потоку все остальные, которые захотят получить его во владение, будут ожидать его освобождения. Отличия заключаются в некоторых нюансах использования — во–первых, объекты mutex могут быть использованы разными процессами (для этого предусмотрены именованные объекты, которые могут быть использованы другими процессами) и, во–вторых, ожидать владения этим объектом можно разными способами — с ограничением по времени или, например, использовать его для синхронизации сразу с несколькими объектами (другими объектами mutex, семафорами, событиями и прочим). Об этом — подробнее в последующих разделах.

События

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

· событиями никто не владеет — то есть устанавливать события в свободное или занятое состояние могут любые потоки, имеющие право доступа к этому объекту

· события различают только два состояния — свободное и занятое. Сколько бы раз вы не переводили событие в занятое состояние, один единственный вызов функции, освобождающей это событие, освободит его. И наоборот.

· в классическом варианте освобождение события разрешает запуск всех потоков, ожидающих его. Объекты исключительного владения и критические секции позволяли возобновить исполнение только одного потока[2] .

· смена состояния события осуществляется в любой момент времени. Так, для вхождения в критическую секцию или для получения объекта mutex во владение необходимо было дождаться их освобождения (что выполнялось автоматически). Для событий это не так.

Соответственно, применительно к событиям, говорят о двух основных состояниях: свободном (установленном) и занятом (сброшенном) и о трех операциях, выполняемых над ними:

· сбросить событие;
Событие в сброшенном состоянии считается занятым. Аналогия — флажок такси (в России — лампа зеленого цвета). Опущенный флаг (или выключенная лампа) означают, что такси занято. Любой поток, имеющий доступ к событию, может сбросить его, независимо от того, какой поток это событие устанавливал.

· установить (иногда — послать) событие;
Установленное (посланное) событие считается свободным. Как только событие освобождается, все ожидающие его потоки могут возобновить свое исполнение (см. сноску). Устанавливать событие может также любой поток.

· дождаться события;
Так как сброс и установка событий происходит в любой момент времени, независимо от предыдущего состояния события, то приходится вводить специальную операцию — дождаться освобождения объекта.

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

Пример. Возвращаясь к примеру с глобальной кучей — для работы с ней необходимо создать составной синхронизирующий объект. Пример такого объекта можно найти в книге “Windows для профессионалов” Джеффри Рихтера. Естественно, логика построения объекта одинакова и в рассматриваемом случае, что позволяет детально сравнить несколько близких решений и заострить внимание на некоторых “мелочах”. Сейчас мы рассмотрим только “скелет” такого объекта и основные правила работы с ним. Позже, после рассмотрения функций Win32 API, рассмотрим конкретную реализацию этого составного объекта и сравним ее с чрезвычайно похожим объектом, приведенным в книге Рихтера.

Сначала попробуем составить представление о тех стандартных объектах, которые надо использовать для построения составного синхронизирующего объекта. Мы имеем дело с потоками–писателями, имеющими исключительный доступ к данным, и потоками–читателями, которые могут иметь одновременный доступ к тем–же данным. Однако наличие хотя–бы одного читателя исключает для писателей возможность доступа к данным.

Из этих соображений следует наличие:

· Критической секции или объекта исключительного владения для синхронизации потоков–писателей. Выбор одного из этих объектов определяется необходимостью исключительного доступа только одного потока–писателя к данным. Удобно также, что освободить секцию или mutex может только тот поток, который его занял. В рассматриваемом примере будем использовать объект mutex, для большей схожести с Рихтером (это позволит подчернуть несколько нюансов в разработке такого объекта).

· События, переходящего в занятое состояние при наличии хотя–бы одного читателя. В данном случае целесообразно выбрать событие, которое будет сбрасываться в занятое состояние при появлении первого потока–читателя и устанавливаться в свободное последним читателем (последним из числа тех, кто пытается осуществить чтение одновременно с другими, но не вообще последнего).

· Счетчика числа потоков–читателей, осуществляющих одновременный доступ. Нулевое значение счетчика соответствует установленному в свободное состояние событию. При увеличении счетчика проверяется его начальное значение, и если оно было 0, то событие сбрасывается в занятое состояние. При уменьшении счетчика проверяется результат — если он 0, то событие устанавливается в свободное состояние.

Можно примерно описать структуру такого объекта (назовем его NEWSWMRG, по сравнению с объектом SWMRG, рассматриваемым у Рихтера — S ingle W riter M ulti R eader G uard). Эта структура должна быть описана примерно так:

struct NEWSWMRG {
СОБЫТИЕ НЕТ_ЧИТАТЕЛЕЙ;
СЧЕТЧИК ЧИСЛО_ЧИТАТЕЛЕЙ;
ОБЪЕКТ_ИСКЛЮЧИТЕЛЬНОГО_ВЛАДЕНИЯ ЕСТЬ_ПИСАТЕЛИ;
};

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

void RequestWriter( NEWSWMRG* p ) {
// дождаться разрешения для писателя
Захватить объект ЕСТЬ_ПИСАТЕЛИ;
// если объект получен во владение — других писателей больше нет
// и пока мы его не освободим — не появятся.
Дождаться события НЕТ_ЧИТАТЕЛЕЙ;
// если событие установлено — читателей также нет
}

К-во Просмотров: 226
Бесплатно скачать Реферат: Синхронизация потоков