Особливості виконання операцій над умовною змінною



Операцію wait викликають, коли деяка умова, необхідна потоку для продовження, не виконується. Умова повинна бути пов’язана зі спільно використовуваними даними. Перед викликом wait потрібно перевірити цю умову в циклі while

while (! condition_expr)               //вираз для умови

wait (condition, mutex);

Розглянемо, чому в цій перевірці використовують цикл while, а не умовний оператор if. На перший погляд, використання while зайве – нам досить перевірити умову один раз, для чого можна скористатися й if.

Пояснимо, чому це не так. Після того, як потік TS виконає операцію signal, потік TW не запускається негайно, а переходить у стан готовності до виконання. Виконуватися він почне тоді, коли його вибере планувальник (звичайно це відбувається швидко). Проте за проміжок часу між викликом signal і початком виконання потоку TW ще один потік (позначимо його TX) може ввійти у критичну секцію (згадаймо, що м’ютекс у цей час поки що відкритий) і змінити в ній дані так, що умова знову перестане виконуватися. Тепер, якби виклик wait стояв за if, після виходу з wait потік TW заблокував би м’ютекс і продовжив своє виконання, незважаючи на те, що умова все одно не виконується. Це помилка, яка практично налагодженню не підлягає (її називають помилковим поновленням – spurious wakeup). Коли ж використати while, то після виходу з wait умова знову буде перевірена, і якщо вона далі не виконується, потік TW розблокує м’ютекс і знову перейде у стан очікування.

Використовуючи while у поєднанні з очікуванням на умовній змінній, ми гарантуємо, що потік продовжить свою роботу тільки під час виконання відповідної умови.

Наведемо приклад використання операції wait для організації очікування, поки звільниться місце у буфері (це фрагмент розв’язання задачі виробників-споживачів, як розглянемо далі). Тут умовою є порожність буфера.

while (count_items_in_buffer() ==n)         //поки буфер повний

wait (not_full, mutex);                             // not_full – умовна змінна

Операція signal повинна викликатися із критичної секції (тим паче, що параметром цієї операції теж є м’ютекс). Потік TS має викликати signal, коли умова, якої очікував потік TW, почне виконуватися.

//тут condition_expr == true

signal(condition, mutex);

Ось приклад використання операції signal для повідомлення про те, що буфер звільнився:

item = receive_from_buffer();                  //звільнити буфер

signal(not_full, mutex);                           //сигналізувати, що буфер непорожній

Розглядаючи цей фрагмент у поєднанні із попереднім, зауважимо: якщо якийсь потік вставить об’єкт у буфер між викликом signal і поновленням виконання потоку, це негайно зробить умову в циклі while знову істинною, внаслідок чого операція wait буде виконана знову.

Операція broadcast може виявитися корисно тоді, коли виконання умови має спричинити негайне розблокування всіх потоків, які очікують. Наприклад, якщо один потік записує у базу даних, а інші чекають, поки він закінчить це робити, щоб прочитати дані з неї (при цьому потоки-читачі один одному не заважають), є сенс після завершення записування виконати саме операцію broadcast, щоб усі читачі змогли негайно розпочати читання.

Відмінності умовних змінних від семафорів

Наведемо принципові відмінності умовних змінних від семафорів, які використовують для організації очікування за умовою.

1. Умовні змінні можуть бути використані лише всередині критичних секцій, при цьому інші потоки можуть входити у критичну секцію під час очікування на умовній змінній. Семафори небезпечно використовувати всередині критичних секцій, оскільки це зазвичай призводить до взаємного блокування.

2. Умовні змінні не зберігають стану, а семафори – зберігають. Ось що це означає.

· Якщо потік TS виконує signal для умовної змінної і потоки, які очікують на цій умовній змінній, відсутні, нічого не відбувається і жодної інформації в системі не зберігають. Коли потім потік TW виконає операцію wait на цій умовній змінній, то він призупиниться.

· Якщо потік TS виконує up для семафора і потоки, які очікують на цьому семафорі, відсутні, значення семафора все одно збільшують і нове значення зберігають у системі. Коли потім потік TW виконає операцію down для цього семафора, він замість призупинення зменшить семафор і продовжить своє виконання (незважаючи на те, що потік TS раніше виконав свою операцію намарно).


Дата добавления: 2018-04-05; просмотров: 303; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!