Семафор (информатика)

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск

Семафо́р — объект, ограничивающий количество потоков, которые могут войти в заданный участок кода. Определение введено Эдсгером Дейкстрой. Семафоры используются при передаче данных через разделяемую память.

Определение семафора[править | править вики-текст]

Семафор — это объект, с которым можно выполнить три операции.

init(n):
счётчик := n
 
enter():
ждать пока счётчик станет больше 0; после этого уменьшить счётчик на единицу.
 
leave():
увеличить счётчик на единицу.

Предположим, что есть такой участок кода:

semaphore.init(5);
// .....
// .....
void DoSomething()
{
    semaphore.enter();
    // .......
    semaphore.leave();
}

Тогда не более пяти потоков могут одновременно выполнять функцию DoSomething().

В более сложных семафорах может использоваться очередь; при этом потоки, ожидающие освобождения семафора, будут проходить через семафор именно в том порядке, в котором они вызывали enter().

Применение семафоров[править | править вики-текст]

Вот некоторые из проблем, которые могут решать семафоры.

  • запрет одновременного выполнения заданных участков кода;
  • поочерёдный доступ к критическому ресурсу (важному ресурсу, для которого невозможен (или нежелателен) одновременный доступ).

Следующий пример показывает, как наладить поочерёдный доступ к консоли.

semaphore.init(1);
 
// Поток 1:
semaphore.enter();
cout << "Состояние массива: ";
for (int i=0; i<n; i++)
    cout << a[i] << ' ';
cout << '\n';
semaphore.leave();
 
// Поток 2:
semaphore.enter();
cout << "Нажато Esc.\n";
semaphore.leave();

Этот код поможет предотвратить появление вывода наподобие

Состояние массива: 1 2 3 Нажато Esc.
4 5 6

Проблемы семафоров[править | править вики-текст]

Во-первых, можно написать программу с «утечкой семафора», вызвав enter() и забыв вызвать leave(). Реже встречаются ошибки, когда дважды вызывается leave().

Во-вторых, семафоры чреваты взаимной блокировкой потоков. В частности, опасен такой код:

// Поток 1:
semaphore1.enter();
semaphore2.enter();
// ...
semaphore2.leave();
semaphore1.leave();
 
// Поток 2:
semaphore2.enter();
semaphore1.enter();
// ...
semaphore1.leave();
semaphore2.leave();

См. также[править | править вики-текст]

Литература[править | править вики-текст]

  • Грегори Р. Эндрюс Основы многопоточного, параллельного и распределённого программирования. — Вильямс, 2003.

Ссылки[править | править вики-текст]