Что такое семафоры в программировании и зачем они нужны?
Семафор — инструмент для управления синхронизацией. Это целочисленная переменная, которую одновременно используют сразу несколько процессов. Основная цель использования семафора — это синхронизация процессов и управление доступом к общему ресурсу в многопроцессорной среде.
У семафоров них есть две основные операции:
- Операция ожидания уменьшает значение аргумента S, если оно положительное. Если значение S отрицательно или равно нулю, операция не выполняется:
wait(S)
{
   while (S<=0);
   S--;
}
- Операция подачи сигнала увеличивает значение своего аргумента S:
signal(S)
{
   S++;
}
Есть два типа семафоров:
- Универсальный или считающий семафор — это семафор с целочисленными значениями и неограниченной областью значений. Эти семафоры используются для координации доступа к ресурсам, где счетчик семафоров — это количество доступных ресурсов. Если ресурсы добавляются, счетчик семафоров автоматически увеличивается, а если ресурсы удаляются, счетчик уменьшается. Считающий семафор используется для работы с несколькими процессами и решения проблем синхронизации.
- Двоичный семафор — имеет значение, ограниченное 0 и 1. Операция ожидания работает только тогда, когда семафор равен 1, а операция сигнала завершается успешно, когда семафор равен 0. Если вам знаком механизм работы мьютексов, то может показаться, что нет разницы, что использовать — двоичный семафор или мьютекс.
Но на самом деле семафор — это сигнальный механизм, а мьютекс — это механизм блокировки.
Работа семафоров на примерах из жизни
Ситуация первая: есть два банкомата, и только два человека одновременно могут снять деньги. Когда человек заходит в банк, он получает разрешение, если имеются свободные ресурсы (банкоматы), и проверяет, какой из банкоматов свободен для использования. Как только он получает доступ к банкомату, то блокирует его, вводит PIN-код и снимает деньги. Только после этого освобождается семафор.
Ситуация вторая: у входа в ресторан стоят 20 человек. В этом случае количество семафоров совпадает с количеством ресурсов (свободных столиков), равным 10. Чтобы клиент мог войти в ресторан, он должен получить разрешение. После этого посетитель выбирает один из доступных столов. Как только его заказ будет выполнен, он освобождает ресурс, делая его доступным для других клиентов в очереди. В этом случае семафор гарантирует, что одновременно только 10 клиентов могут войти в ресторан и сделать заказ.
В обоих случаях семафор проверяет наличие доступных ресурсов и блокирует возможность их использовать, если лимит ресурсов исчерпан. Когда количество ресурсов становится больше нуля, цикл повторяется и семафор выдает разрешение на их использование.
Пример кода с использованием семафоров
Давайте посмотрим, как семафоры работают в коде C++:
#include<iostream>
#include<mutex>
using namespace std;
struct semaphore
{
    int mutex;
    int rcount;
    int rwait;
    bool wrt;
};
void addR(struct semaphore *s)
{
    if(s->mutex == 0 && s->rcount == 0)
    {
        cout<<"\nSorry, File open in Write mode.\nNew Reader added to queue.\n";
        s->rwait++;
    }
    else
    {
        cout<<"\nReader Process added.\n";
        s->rcount++;
        s->mutex--;
    }
return ;
}
void addW(struct semaphore *s)
{
    if(s->mutex==1)
    {
        s->mutex--;
        s->wrt=1;
        cout<<"\nWriter Process added.\n";
    }
    else if(s->wrt) 
        cout<<"\nSorry, Writer already operational.\n";
    else
        cout<<"\nSorry, File open in Read mode.\n";
return ;
}
void remR(struct semaphore *s)
{
    if(s->rcount == 0) cout<<"\nNo readers to remove.\n";
    else
    {
        cout<<"\nReader Removed.\n";
        s->rcount--;
        s->mutex++;
    }
return ;
}
void remW(struct semaphore *s)
{
    if(s->wrt==0) cout<<"\nNo Writer to Remove\n";
   else
   {
        cout<<"\nWriter Removed\n";
        s->mutex++;
        s->wrt=0;
  if(s->rwait!=0)
{
    s->mutex-=s->rwait;
    s->rcount=s->rwait;
    s->rwait=0;
    cout<<"waiting Readers Added:"<<s->rcount<<endl;
}
}
}
int main()
{
struct semaphore S1={1,0,0};
while(1)
{
cout<<"Options :-\n1.Add Reader.\n2.Add Writer.\n3.Remove Reader.\n4.Remove Writer.\n5.Exit.\n\n\tChoice : ";
int ch;
cin>>ch;
switch(ch)
{
case 1: addR(&S1); break;
case 2: addW(&S1); break;
case 3: remR(&S1); break;
case 4: remW(&S1); break;
case 5: cout<<"\n\tGoodBye!";break;
default: cout<<"\nInvalid Entry!";
}
}
return 0;
}
В результате получим:
Options :-
1.Add Reader.
2.Add Writer.
3.Remove Reader.
4.Remove Writer.
5.Exit.
        Choice : 1
Reader Process added.
Options :-
1.Add Reader.
2.Add Writer.
3.Remove Reader.
4.Remove Writer.
5.Exit.
        Choice : 1
Reader Process added.
Options :-
1.Add Reader.
2.Add Writer.
3.Remove Reader.
4.Remove Writer.
5.Exit.
        Choice : 1
Reader Process added.
Options :-
1.Add Reader.
2.Add Writer.
3.Remove Reader.
4.Remove Writer.
5.Exit.
        Choice : 3
Reader Removed.
Options :-
1.Add Reader.
2.Add Writer.
3.Remove Reader.
4.Remove Writer.
5.Exit.
        Choice : 2
Sorry, File open in Read mode.
Options :-
1.Add Reader.
2.Add Writer.
3.Remove Reader.
4.Remove Writer.
5.Exit.
        Choice : 3
Reader Removed.
Options :-
1.Add Reader.
2.Add Writer.
3.Remove Reader.
4.Remove Writer.
5.Exit.
            		 
        
        
    




 
    
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: