Особливості використання семафорів
Семафори можна використовувати для розв’язання двох різних задач синхронізації.
- За їхньою допомогою можна організовувати взаємне виключення (захищати код критичних секцій від виконання більш як одним потоком). Найзручніше для цього використати двійковий семафор, який може приймати два значення: 0 і 1. Ось приклад реалізації критичної секції з використанням двійкового семафора:
semaphore_t sem = 1; //на початку семафор відкритий
down(sem);
//критична секція
up(sem);
- За їхньою допомогою можна організувати очікування виконання деякої умови. Припустимо, що треба організувати очікування одним потоком завершення виконання іншого (аналог операції приєднання). У цьому випадку можна використати семафор із початковим значенням 0 (закритий). Потік, який чекатиме повинен виконати для цього семафора операцію down, а щоб просигналізувати про завершення потоку, у його функції завершення потрібно для того самого семафора виконати up. Ось псевдокод (this_thread означає поточний потік):
void thread_init() {
this_thread.sem = 0; //на початку виконання потоку семафор закритий
}
void thread_exit() {
up(this_thread.sem); //розбудити потік, що очікує, якщо такий є
}
void thread_join(thread_t thread) {
down(thread.sem); //очікування завершення потоку thread
}
Реалізація задачі виробників-споживачів за допомогою семафорів
Спочатку назвемо синхронізаційні дії, потрібні для розв’язання цієї задачі.
|
|
- Помістити операції безпосередньої зміни буфера(для виробника – поміщення об’єкта у буфер, для споживача – вилучення об’єкта із буфера) у критичні секції.
- Організувати очікування відповідно до вимоги 2 (очікування виробника у разі повного буфера). При цьому споживач повинен повідомляти виробникам, які очікують, про те, що він забрав об’єкт із буфера (тобто буфер став неповним, якщо він був певним).
- Організувати очікування відповідно до вимоги 3 (очікування споживача у разі порожнього буфера). При цьому виробник повинен повідомляти споживачів, які очікують, про те, що він додав новий об’єкт у буфер(тобто буфер став непорожнім, якщо він був порожнім).
Тепер розглянемо синхронізаційні примітиви, які нам потрібні. Кожна синхронізаційна операція потребує окремого семафора.
- Для організації критичної секції використаємо двійковий семафор, як це робили раніше. Назвемо його lock. Він використовуватиметься як виробником, так і споживачем, захищаючи доступ до буфера від інших потоків (знову таки, як виробників, так і споживачів).
- Для організації очікування виробника у разі повного буфера нам знадобиться семафор, поточне значення якого дорівнює кількості вільних місць у буфері. Назвемо його empty_items. Виробник перед спробою додати новий об’єкт у буфер зменшує цей семафор, переходячи в стан очікування, якщо той дорівнював 0. Споживач після того, як забере об’єкт із буфера, збільшить семафор, повідомивши таким способом про це виробників, що очікують (і розбудивши одного із них).
- Для організації очікування споживача у разі порожнього буфера знадобиться семафор, поточне значення якого дорівнює кількості зайнятих місць у буфері. Назвемо його full_items. Споживач перед спробою забрати об’єкт із буфера зменшує цей семафор, переходячи у стан очікування, якщо той дорівнював 0. Виробник після того, як додасть об’єкт у буфер, збільшить семафор, повідомивши таким способом про це споживачів, що очікують (і розбудивши одного з них). Ось псевдокод розв’язання цієї задачі:
semaphore_t lock = 1; //для критичної секції
|
|
semaphore_t empty_items = n; //0 – буфер повний, від початку він порожній
semaphore_t full_items = 0; //якщо 0 – буфер порожній
//виробник
void producer() {
item_t item = produce(); //створити об’єкт
down(empty_items); //чи є місце для об’єкта?
down(lock); //вхід у критичну секцію
|
|
append_to_buffer(item); //додати об’єкт item у буфер
up(lock); //вихід із критичної секції
up(full_items); //повідомити споживачів, що є новий об’єкт
}
//споживач
void consumer() {
item_t item;
down(full_items); //чи не порожній буфер?
down(lock); //вхід у критичну секцію
item = receive_from_buffer(); //забрати об’єкт item з буфера
up(lock); //вихід із критичної секції
up(empty_items); //повідомити виробників, що є місце
consume(item); //спожити об’єкт
}
Дата добавления: 2018-04-05; просмотров: 406; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!