Що таке семафор?


350

Семафор - це концепція програмування, яка часто використовується для вирішення завдань із багатопотоковою ниткою. Моє запитання до громади:

Що таке семафор і як ним користуватися?


14
булевий прапор, значення якого базується на тому, чи досяг цілочисельний лічильник своєї визначеної верхньої межі. Заплутаність до максимуму!
Сем,

Відповіді:


400

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

Це просто спосіб обмеження кількості споживачів для конкретного ресурсу. Наприклад, обмежити кількість одночасних дзвінків до бази даних в додатку.

Ось дуже педагогічний приклад на C # :-)

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace TheNightclub
{
    public class Program
    {
        public static Semaphore Bouncer { get; set; }

        public static void Main(string[] args)
        {
            // Create the semaphore with 3 slots, where 3 are available.
            Bouncer = new Semaphore(3, 3);

            // Open the nightclub.
            OpenNightclub();
        }

        public static void OpenNightclub()
        {
            for (int i = 1; i <= 50; i++)
            {
                // Let each guest enter on an own thread.
                Thread thread = new Thread(new ParameterizedThreadStart(Guest));
                thread.Start(i);
            }
        }

        public static void Guest(object args)
        {
            // Wait to enter the nightclub (a semaphore to be released).
            Console.WriteLine("Guest {0} is waiting to entering nightclub.", args);
            Bouncer.WaitOne();          

            // Do some dancing.
            Console.WriteLine("Guest {0} is doing some dancing.", args);
            Thread.Sleep(500);

            // Let one guest out (release one semaphore).
            Console.WriteLine("Guest {0} is leaving the nightclub.", args);
            Bouncer.Release(1);
        }
    }
}

6
якщо це як вистрибувачі в нічному клубі, він повинен відпускати гостей послідовно, але коли я спробував це, це випадково. Напр. Гість 40 прийшов першим перед гостем 39. Чи є щось, що ми могли б зробити, щоб це контролювати?
TNA

2
@TNA: Так, це стосується того, як у цьому прикладі запускаються нові потоки, а не в межах відповіді.
Патрік Свенссон


Яке значення пропонують семафори в розподілених системах?
csandreas1

198

Стаття « Мутекси та семафори, демістифіковані Майклом Барром» - це чудовий короткий вступ до того, що робить мутекси та семафори різними, і коли вони повинні і не повинні використовуватися. Тут я виклав кілька основних абзаців.

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

Хоча мутекси та семафори мають певну схожість у своїй реалізації, їх завжди слід використовувати по-різному.

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

...

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

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

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

...

Правильне використання семафору - для передачі сигналу від одного завдання до іншого. Мутекс має бути взято та випущено, завжди в такому порядку, кожним завданням, що використовує спільний ресурс, який він захищає. Навпаки, завдання, які використовують семафори або сигналізують, або чекають - не обидва. Наприклад, Завдання 1 може містити код для розміщення (тобто сигналу або збільшення) певного семафору при натисканні кнопки "живлення", а завдання 2, яке пробуджує дисплей, очікує на цей же семафор. У цьому сценарії одним завданням є виробник сигналу події; інший споживач.

...

Тут важливим моментом є те, що мютекси погано втручаються в операційні системи в режимі реального часу, викликаючи інверсію пріоритетів, коли перед важливішим завданням може бути виконано менш важливе завдання через обмін ресурсами. Коротше кажучи, це трапляється, коли завдання з нижчим пріоритетом використовують мьютекс для захоплення ресурсу A, потім намагаються схопити B, але призупинено, оскільки B недоступний. Поки воно чекає, вирішується завдання з більш високим пріоритетом і потребує A, але воно вже пов'язане, і процес, який навіть не працює, тому що він чекає B. Є багато способів вирішити це, але найчастіше це виправлено змінивши мютекс та менеджер завдань. Мутекс у цих випадках набагато складніший, ніж двійковий семафор,

...

Причина широко поширеної сучасної плутанини між мютексами та семафорами є історичною, оскільки вона сягає всього часу винаходом Джикстра 1974 року Семафору (столиця "S"). До цієї дати жоден із механізмів синхронізації та сигналізації, захищених від перерв, відомих комп'ютерним вченим, не був ефективно масштабованим для використання більш ніж двома завданнями. Революційний, безпечний і масштабований семафор Dijkstra застосовувався як у захисті критичної секції, так і в сигналізації. І таким чином почалася плутанина.

Однак це стало очевидним для розробників операційної системи після появи пріоритетного RTOS на основі пріоритетів (наприклад, VRTX, ок. 1980 р.), Публікації наукових праць, що встановлюють RMA, та проблем, спричинених інверсією пріоритету, та документу про пріоритет у протоколах успадкування 1990 р. [3] стало очевидно, що мутекси повинні бути більше, ніж просто семафори з бінарним лічильником.

Mutex: обмін ресурсами

Семафор: сигналізація

Не використовуйте один для іншого без уважного розгляду побічних ефектів.


10
Подивіться цей PDF документ про одночасність Стенфорда. Подивіться на сторінках 8. Наведене вище опис буде мати більше сенсу тоді .. see.stanford.edu/materials/icsppcs107 / ...
Kris Субраманян

3
Книжечка семафорів є цінним читати про ці питання.
Г. Бах

@KrisSubramanian Дякую за посилання. Але в документі йдеться про семафори та нічого про Mutexes. Однак ви маєте на увазі, що спільний буфер у прикладі може бути захищений за допомогою Mutex? замість того, щоб мати 2 семафори порожніБуфер і fullBuffers
talekeDskobeDa

1
@Pramod True. Посилання не додає жодних приміток, пов’язаних з Mutex. Я додав посилання для того, щоб семафорна сторона речей стала зрозумілою для читачів SO. :) Цікаво, що в цьому випадку буфер використовується без будь-яких блокувань, оскільки до нього звертаються послідовно та в круговому форматі. тобто Writer запише до 0 і подасть сигнал читачеві прочитати з 0. Якщо читач не читає з 0 і подасть сигнал письменнику, тоді він записує. Тому немає необхідності використовувати мьютекс для блокування загального ресурсу. Це відрізняється від аналогії ванної, наведеної вище.
Крис Субраманян

@Kris Subramanian: приємний документ, але включає невеликі помилки: на 3-й сторінці починається повідомлення про те, що "кожна нитка, що замикає семафор, повинна бути обережною, щоб розблокувати її" - їх можна розблокувати будь-якою ниткою. Якщо ви робите це в тій самій нитці, ви просто використовуєте його як "brocken mutex". "Брокен", тому що його все одно можна ненавмисно відімкнути від іншого потоку - трапляються помилки - і порушити вашу логіку. Ще приємний доктор, подумав.
Рустам А.

70

Mutex: ексклюзивний доступ до ресурсу

Семафор: доступ n-членів до ресурсу

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

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


20
За словами Річарда В. Стівенса, мютекс насправді є бінарним семафором, маючи лише два можливі значення: 0 і 1.
Цян Сю

19
@QiangXu в операційних системах і внутрішніх принципах дизайну Вільяма Сталлінга, бінарний семафор відрізняється від mutex одним дуже важливим способом, і я цитую: "Ключова різниця між мютекс і бінарним семафором полягає в тому, що процес блокує мютекс він повинен бути тим, хто її розблокує. На противагу цьому, один процес може заблокувати двійковий семафор, а інший - розблокувати його ". .
Електрична кава

3
Загрожуючи коментувати несвіжу нитку, це неправильно. Як згадував @AdamDavis вище, Semaphore не повинен (повинен бути) використаний для доступу n-членів до ресурсу - це все одно слід робити за допомогою Mutex. Розглянемо аналогію ванної кімнати в Coffeeshop з кількома людьми, які чекають доступу або в декількох санвузлах, подібними ключами від ванних кімнат. Швидше Семафор слід використовувати для передачі сигналів між завданнями.
cspider

21

Подумайте, таксі, яке може вмістити всього 3 ( ззаду ) +2 ( спереду ) осіб, включаючи водія. Так, одночасно semaphoreдозволяє автомобіль лише 5 осіб всередині автомобіля. А mutexна одному сидінні автомобіля допускається лише 1 людина.

Тому Mutexдозволити винятковий доступ до ресурсу ( наприклад, потоку ОС ), а а Semaphore- дозволити доступ до n кількості ресурсів одночасно.


19

@Craig:

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

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


7
Це коментар, а не відповідь.
касперський

11
Так, але я думаю, що я написав це до того, як коментарі були додані до Stack Overflow. Або я цього не зробив. Цього разу я відповів у коментарі. :-)
Матс Фредрікссон

16

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

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

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


11

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

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

Семафор має дві частини: лічильник та перелік завдань, які чекають доступу до певного ресурсу. Семафор виконує дві операції: зачекати (P) [це як придбати замок], і відпустити (V) [подібно до звільнення блокування] - це єдині дві операції, які можна виконати на семафорі. У двійковому семафорі лічильник логічно переходить між 0 і 1. Ви можете вважати, що він схожий на замок з двома значеннями: відкритий / закритий. Підрахунок семафору має кілька значень для підрахунку.

Важливо розуміти, що лічильник семафору відстежує кількість завдань, які не доведеться блокувати, тобто вони можуть прогресувати. Завдання блокують і додають себе до списку семафору лише тоді, коли лічильник дорівнює нулю. Тому завдання додається до списку в рутині P (), якщо він не може прогресувати, та "звільняється" за допомогою програми V ().

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

колишній Синхронізація:

thread A{
semaphore &s; //locks/semaphores are passed by reference! think about why this is so.
A(semaphore &s): s(s){} //constructor
foo(){
...
s.P();
;// some block of code B2
...
}

//thread B{
semaphore &s;
B(semaphore &s): s(s){} //constructor
foo(){
...
...
// some block of code B1
s.V();
..
}

main(){
semaphore s(0); // we start the semaphore at 0 (closed)
A a(s);
B b(s);
}

У наведеному вище прикладі B2 може бути виконаний лише після завершення виконання B1. Скажімо, нитка A приходить виконується спочатку - дістається до sem.P () і чекає, оскільки лічильник 0 (закритий). Нитка B йде уздовж, закінчує B1, а потім звільняє нитку A - яка потім завершує B2. Таким чином ми досягаємо синхронізації.

Тепер давайте розглянемо взаємне виключення з бінарним семафором:

thread mutual_ex{
semaphore &s;
mutual_ex(semaphore &s): s(s){} //constructor
foo(){
...
s.P();
//critical section
s.V();
...
...
s.P();
//critical section
s.V();
...

}

main(){
semaphore s(1);
mutual_ex m1(s);
mutual_ex m2(s);
}

Взаємне виключення також досить просте - m1 і м2 не можуть одночасно увійти в критичну секцію. Таким чином, кожна нитка використовує один і той же семафор, щоб забезпечити взаємне виключення двох своїх критичних ділянок. Тепер, чи можна мати більшу сумісність? Залежить від критичних розділів. (Подумайте, як ще можна було б використовувати семафори для досягнення взаємного виключення. Підказка: чи мені обов’язково потрібно використовувати лише один семафор?)

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

Візьмемо простіше з двох:

Синхронізація за допомогою рахункового семафору. Скажімо, у вас є 3 завдання - №1 та 2, які ви хочете виконати після 3. Як би ви створили свою синхронізацію?

thread t1{
...
s.P();
//block of code B1

thread t2{
...
s.P();
//block of code B2

thread t3{
...
//block of code B3
s.V();
s.V();
}

Отже, якщо ваш семафор починається закритим, ви гарантуєте, що блок t1 і t2 перейти до списку семафору. Тоді разом йде все важливе t3, закінчує свою справу і звільняє t1 і t2. У якому порядку вони звільняються? Залежить від виконання списку семафору. Може бути FIFO, може базуватися на якомусь конкретному пріоритеті тощо. (Примітка: подумайте, як би ви упорядкували свої P і V; s, якщо ви хочете, щоб t1 і t2 були виконані в певному порядку, і якщо ви не знали про реалізацію семафору)

(Дізнайтеся: що станеться, якщо число V більше, ніж число P?)

Взаємне виключення Використання підрахунку семафорів: Я хотів би, щоб ви створили для цього свій псевдокод (змушує вас зрозуміти речі краще!) - але основна концепція така: підрахунок семафору лічильника = N дозволяє N завданням вільно входити до критичного розділу . Це означає, що ви маєте N завдань (або потоків, якщо вам подобається), перейдіть до критичного розділу, але N + 1-е завдання блокується (переходить у наш улюблений список заблокованих завдань), і воно пропускається лише тоді, коли хтось із V має семафор принаймні, один раз. Тож лічильник семафору замість того, щоб коливатися між 0 і 1, тепер переходить між 0 і N, дозволяючи N завданням вільно входити і виходити, нікого не блокуючи!

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

Поміркувати : чи досягається взаємне виключення, маючи один підрахунок семафору? Що робити, якщо у вас є 10 екземплярів ресурсу, і 10 ниток входять (через семафор рахунку) і намагаються використовувати перший екземпляр?


7

Семафор - це об'єкт, що містить натуральне число (тобто ціле число, більший або дорівнює нулю), на якому визначено дві модифікуючі операції. Одна операція, Vдодає 1 до природного. Інша операція Pзменшує натуральне число на 1. Обидві дії є атомними (тобто жодна інша операція не може бути виконана одночасно з a Vабо a P).

Оскільки природне число 0 неможливо зменшити, виклик Pсемафору, що містить 0, заблокує виконання процесу виклику (/ потоку) до певного моменту, коли число більше не буде 0 і Pможе бути успішно (і атомним чином) виконане.

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


7

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

ExecutorService executor = Executors.newFixedThreadPool(7);

Semaphore semaphore = new Semaphore(4);

Runnable longRunningTask = () -> {
    boolean permit = false;
    try {
        permit = semaphore.tryAcquire(1, TimeUnit.SECONDS);
        if (permit) {
            System.out.println("Semaphore acquired");
            Thread.sleep(5);
        } else {
            System.out.println("Could not acquire semaphore");
        }
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    } finally {
        if (permit) {
            semaphore.release();
        }
    }
};

// execute tasks
for (int j = 0; j < 10; j++) {
    executor.submit(longRunningTask);
}
executor.shutdown();

Вихідні дані

Semaphore acquired
Semaphore acquired
Semaphore acquired
Semaphore acquired
Could not acquire semaphore
Could not acquire semaphore
Could not acquire semaphore

Зразок коду зі статті


3

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


2

Семафори діють як обмежувачі ниток.

Приклад: Якщо у вас є пул з 100 потоків і ви хочете виконати деяку операцію з БД. Якщо 100 потоків отримують доступ до БД в даний момент часу, можливо, в БД може виникнути проблема блокування, тому ми можемо використовувати семафор, який дозволяє лише обмежений потік одночасно. Нижче наведений приклад дозволяє одночасно мати один потік. Коли потік викликає acquire()метод, він отримає доступ і після виклику release()методу він випустить доступ, щоб наступний потік отримав доступ.

    package practice;
    import java.util.concurrent.Semaphore;

    public class SemaphoreExample {
        public static void main(String[] args) {
            Semaphore s = new Semaphore(1);
            semaphoreTask s1 = new semaphoreTask(s);
            semaphoreTask s2 = new semaphoreTask(s);
            semaphoreTask s3 = new semaphoreTask(s);
            semaphoreTask s4 = new semaphoreTask(s);
            semaphoreTask s5 = new semaphoreTask(s);
            s1.start();
            s2.start();
            s3.start();
            s4.start();
            s5.start();
        }
    }

    class semaphoreTask extends Thread {
        Semaphore s;
        public semaphoreTask(Semaphore s) {
            this.s = s;
        }
        @Override
        public void run() {
            try {
                s.acquire();
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+" Going to perform some operation");
                s.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } 
    }

1

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

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

Існують також відмінності між бінарними / мутексними та підрахунковими семафорами.

Перегляньте конспекти лекцій на веб- сайті http://www.cs.columbia.edu/~jae/4118/lect/L05-ipc.html .


0

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

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

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

Далі читайте :


-3

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


13
звучить як мутекс, а не семафор
Сем,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.