Как работает семафор? - PullRequest
13 голосов
/ 03 августа 2009

Может ли семафор быть ниже 0? Я имею в виду, скажем, у меня есть семафор с N = 3, и я называю «вниз» 4 раза, тогда N останется 0, но один процесс будет заблокирован?

И наоборот, если в начале я позвоню, может ли N быть больше 3? Потому что, как я понимаю, если N может быть больше 3, если вначале я вызываю пару раз, то позже я могу вызывать больше раз, чем могу, таким образом помещая больше процессов в критическую секцию, чем позволяет семафор .

Если кто-то немного прояснит это для меня, я буду очень признателен.

Грег

Ответы [ 6 ]

17 голосов
/ 03 августа 2009

(Используя терминологию из java.util.concurrent.Semaphore, заданную тегом Java. Некоторые из этих деталей зависят от реализации. Я подозреваю, что ваше «down» - это метод acquire() семафора Java, а ваше «up» - release().)

Да, ваш последний вызов acquire() будет блокироваться, пока другой поток не вызовет release() или ваш поток не будет прерван.

Да, вы можете звонить release() больше раз, а затем - больше, по крайней мере, с java.util.concurrent.Semaphore.

Некоторые другие реализации семафора могут иметь представление о «максимальном» количестве разрешений, и вызов для освобождения сверх этого максимума будет неудачным. Класс Java Semaphore допускает обратную ситуацию, когда семафор может начинаться с отрицательного числа разрешений, и все вызовы acquire() завершатся неудачно, пока не будет достаточно вызовов release(). Как только количество разрешений станет неотрицательным, оно никогда не станет отрицательным.

8 голосов
/ 06 ноября 2012

Привет, Грег, рассмотри следующий пример:

public static void main(String [] args) throws InterruptedException {

        Semaphore available = new Semaphore(1, true);

        available.acquire();
        System.out.println("Acquire : " +available.availablePermits());

        available.release();
        System.out.println("Released : " +available.availablePermits());

        available.release();
        System.out.println("Released : " +available.availablePermits());

        available.release();
        System.out.println("Released : " +available.availablePermits());

        available.release();
        System.out.println("Released : " +available.availablePermits());

        available.acquire();
        System.out.println("Acquire : " +available.availablePermits());

        available.acquire();
        System.out.println("Acquire : " +available.availablePermits());

        available.acquire();
        System.out.println("Acquire : " +available.availablePermits());

        available.acquire();
        System.out.println("Acquire : " +available.availablePermits());

        available.acquire();
        System.out.println("Acquire : " +available.availablePermits());
    }

Если вы видите вывод, вы получите следующее:

Acquire : 0
Released : 1
Released : 2
Released : 3
Released : 4
Acquire : 3
Acquire : 2
Acquire : 1
Acquire : 0

И ожидание продолжается.

Таким образом, разрешение будет увеличиваться с каждым выпуском, а приобретение будет уменьшаться до 0. Как только он достигнет 0, он будет ждать, пока не будет вызван релиз для того же объекта :)

6 голосов
/ 03 августа 2009

Вызов, когда он равен 0, не должен работать. Вызов, когда это 3 работает. (Я думаю о Java).

Позвольте мне добавить еще. Многие люди думают о блокировках, подобных (двоичным) семафорам (то есть - N = 1, поэтому значение семафора равно 0 (удерживается) или 1 (не удерживается)). Но это не совсем верно. В замке есть понятие «владение», поэтому он может быть «повторно входящим». Это означает, что потоку, который содержит блокировку, разрешено снова вызывать lock () (эффективно перемещая счетчик от 0 до -1), поскольку поток уже удерживает блокировку и ему разрешено «повторно вводить» ее. Замки также могут быть не реентерабельными. Ожидается, что держатель блокировки будет вызывать unlock () столько же раз, сколько и lock ().

Семафоры не имеют понятия собственности, поэтому они не могут быть повторно введены, хотя может быть получено столько разрешений, сколько доступно. Это означает, что поток должен блокироваться, когда он встречает значение 0, пока кто-то не увеличит семафор.

Кроме того, в том, что я видел (это Java), вы можете увеличить семафор больше, чем N, и это также как-то связано с владением: у семафора нет понятия владения, поэтому любой может дать ему больше разрешений , В отличие от потока, где всякий раз, когда поток вызывает unlock () без удержания блокировки, это ошибка. (В Java это будет исключение).

Надеюсь, этот способ мышления поможет.

2 голосов
/ 03 августа 2009

Да, отрицательное значение означает, что у вас есть процессы, ожидающие освобождения семафора. Положительное значение означает, что вы можете вызывать функцию acqu много раз, прежде чем блокировать семафор.

Вы можете думать о значении следующим образом: положительное число означает, что доступно столько ресурсов. Отрицательное значение означает, что есть много объектов, нуждающихся в ресурсе, когда все ресурсы используются в данный момент. Когда вы приобретаете ресурс, вы уменьшаете его значение, а когда вы его отпускаете, вы увеличиваете его. Если значение по-прежнему> = 0 после уменьшения, вы получаете ресурс, в противном случае ваша сущность помещается в очередь.

Хорошее объяснение семафоров в Википедии: http://en.wikipedia.org/wiki/Semaphore_(programming)

1 голос
/ 03 августа 2009

Просто посмотрите N как счетчик, который считает ваш ограниченный ресурс. Поскольку у вас не может быть отрицательного количества ресурсов, N остается> = 0. Если количество ваших доступных ресурсов изменяется, максимальное N также должно быть изменено. Я не считаю хорошим вариантом увеличивать n, не уменьшая его сначала в любом другом случае.

0 голосов
/ 02 ноября 2016

Используя java.util.concurrent.Semaphore с методами acquire() и release(), я думаю, что разрешения всегда будут >=0. Допустим, вы хотите синхронизировать потоки, так что только один поток может быть внутри цикла. Если sem имеет тип Semaphore с начальным значением 1, это не будет работать для более чем 2 потоков.

while(true){            

    sem.wait(); // wait is acquire

    for(int i=0; i<=5; i++){

        try {
            Thread.sleep(250);
        }catch (InterruptedException e) {}

        System.out.println("Thread "+ threadname+ " " + i);

             }
    sem.signal(); // signal is release }

Однако вы можете реализовать класс Semaphore из Java и создать свой собственный класс, который позволяет это.

package yourpackage;

import java.util.concurrent.Semaphore;

public class SemaphoreLayer {
public Semaphore s=null;
public String name;
private int val;

public SemaphoreLayer(int i){
    s=new Semaphore(i); val=i;
}

public void wait(){
 try {
     val--;
     s.acquire();

  } catch (InterruptedException e) {
    System.out.println("Error signal semaphorelayer");
}}

public void signal(){
    if(val<0){val++;}{
        s.release();
        val++;
    }
}

}

Теперь val может быть отрицательным. Однако я не уверен, что это абсолютно безопасно, потому что если у нас есть сигнал от одного потока и мы ждем от другого, и они пытаются val++ и val--, это может быть плохо. (шансы на это очень малы, но все же они существуют, поэтому, если вы кодируете, и вы должны быть на 100% без ошибок, я не рекомендую использовать этот код) В заключение, вот почему лучше использовать концепцию мониторов в Java и синхронизировать ключевое слово.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...