Умовна змінна проти семафору


Відповіді:


207

Замки використовуються для взаємного виключення. Коли ви хочете переконатися, що фрагмент коду є атомним, поставте навколо нього замок. Ви можете теоретично використовувати двійковий семафор для цього, але це особливий випадок.

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

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

//pseudocode
while(!queue.empty())
{
   sleep(1);
}

Проблема з цим полягає в тому, що ви витрачаєте процесорний час, повторюючи умови поточної перевірки. Чому б замість цього не було змінної синхронізації, яку можна сигналізувати, щоб повідомити потоку про те, що ресурс доступний?

//pseudocode
syncVar.lock.acquire();

while(!queue.empty())
{
   syncVar.wait();
}

//do stuff with queue

syncVar.lock.release();

Імовірно, у вас буде десь інша тема, яка витягує речі з черги. Коли черга порожня, вона може зателефонувати, syncVar.signal()щоб прокинутись випадковою ниткою, яка сидить уві сні syncVar.wait()(або зазвичай також є signalAll()або broadcast()метод, щоб прокинути всі потоки, які чекають).

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

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

Подумайте, чи був у вас автомат з газованою содою. Є лише одна содова машина, і це спільний ресурс. У вас є одна нитка - це постачальник (виробник), який відповідає за збереження машини на складі, і N ниток - покупці (споживачі), які хочуть дістати газовані напої з машини. Кількість газованих напоїв у машині - це ціле значення, яке призведе до нашого семафору.

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

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

Методи wait()і signal()змінної синхронізації, як правило, приховані в семафорі down()та up()операціях.

Звичайно, між двома варіантами є перекриття. Існує багато сценаріїв, коли семафор або змінна умова (або набір змінних умов) можуть обидва служити вашим цілям. І семафори, і змінні умови асоціюються з об'єктом блокування, який вони використовують для підтримки взаємного виключення, але потім вони надають додаткову функціональність поверх блокування для синхронізації виконання потоку. В основному, ви повинні визначити, який з них має найбільш сенс для вашої ситуації.

Це не обов'язково найбільш технічний опис, але саме так це має сенс у моїй голові.


9
Чудова відповідь, я хотів би додати з інших таких відповідей: Семафор використовується для управління кількістю виконуваних потоків. Буде закріплений набір ресурсів. Кількість ресурсів зменшуватиметься кожного разу, коли потоці належить те саме. Коли кількість семафорів досягає 0, жодним іншим потоком не дозволяється придбати ресурс. Нитки блокуються до випуску інших потоків, що володіють ресурсами. Коротше кажучи, головна відмінність полягає в тому, скільки ниток дозволено придбати ресурс одночасно? Mutex --свій ОДИН. Семафор - його DEFINED_COUNT, (скільки рахується семафор)
berkay

10
Просто уточнити, чому існує цей цикл, а не простий if: щось називається spurios wakeup . Цитуючи цю статтю у Вікіпедії : "Однією з причин цього є хибне пробудження; тобто, нитка може бути пробуджена зі свого стану очікування, хоча жодна нитка не сигналізує про змінну умову"
Владислав Бураков

3
@VladislavsBurakovs Добрий момент! Я думаю, що це також корисно для випадку, коли в ефірі прокидається більше ниток, ніж є доступних ресурсів (наприклад, трансляція прокидається 3 потоки, але в черзі є лише 2 елементи).
Код Брент пише

Я б хотів, щоб я просив вас відповісти до повної черги;) Ідеальний відповідь. Цей код може допомогти з'ясувати семафори csc.villanova.edu/~mdamian/threads/PC.htm
Mohamad-Jaafar NEHME

3
@VladislavsBurakovs Щоб трохи уточнити, причина того, що умова все-таки може бути помилковою для потоку, який щойно прокинувся (в результаті чого виникає помилкове пробудження), полягає в тому, що, можливо, відбувся контекстний перемикач, перш ніж нитка отримала можливість перевірити стан знову, де якась інша запланована нитка зробила цю умову помилковою. Це одна з причин, що я знаю, щоб неправдоподібно прокинутися, не знаю, чи є ще.
Макс

52

Розкриємо, що знаходиться під кришкою.

Умовна змінна, по суті, є чергою очікування , яка підтримує операції блокування-очікування та пробудження, тобто ви можете помістити потік у чергу очікування та встановити її стан на БЛОК, і отримати нитку з неї та встановити її стан НАСТОЯТИЙ.

Зауважте, що для використання умовної змінної потрібні два інші елементи:

  • умова (зазвичай реалізується шляхом перевірки прапорця чи лічильника)
  • мутекс, що захищає стан

Потім протокол стає,

  1. набувають мутекс
  2. перевірити стан
  3. блокуйте та вивільнюйте mutex, якщо умова справжня, в іншому випадку випустіть mutex

Семафор - це по суті лічильник + мутекс + черга очікування. І його можна використовувати як без зовнішніх залежностей. Ви можете використовувати його як мутекс або як умовну змінну.

Тому семафор можна розглядати як більш досконалу структуру, ніж умовну змінну, тоді як остання є більш легкою та гнучкою.


mutex може розглядатися як змінна умова, умова полягає в тому, чи буде утримуватися чи ні.
宏杰 李

18

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

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


9

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

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

змінна умова з іншого боку, є ідеальною. коли кожен потік потрапляє до бар'єру, ми перевіряємо, чи є наш статичний лічильник нульовим. якщо ні, то ми встановлюємо потік для сну з функцією очікування змінної умови. коли останній потік потрапить на бар'єр, значення лічильника зменшиться до нуля, і цей останній потік викликає функцію сигналу змінної умови, яка прокине всі інші потоки!


1

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

Я схильний кодувати до форми монітора; у більшості мов, на яких я працюю, зводиться до мутексів, змінних умов та деяких змінних резервного стану. Але семафори теж зробили б свою роботу.


2
Це буде кращою відповіддю, якби ви пояснили, що таке "форма монітора".
Стівен Лу

0

Ті mutexі conditional variablesуспадковуються від semaphore.

  • Для mutex, в semaphoreвикористанні двох станів: 0, 1
  • Для отримання condition variablesв semaphore лічильнику використовує.

Вони схожі на синтаксичний цукор


У бібліотеці st + C ++ вони є всіма об'єктами району, всі реалізовані за допомогою API, визначених для платформи. Безумовно, семафор розблокує кількість сигналів, змінна стану може бути сигналізована кілька разів, але розблокується лише один раз. Ось чому вказівник приймає мютекс як параметр.
Дорон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.