Как работает семафор?

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

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

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

Грег

Ответы

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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, оно будет ждать вызова вызова для того же объекта :)

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

Используя 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 и ключевое слово синхронизированы.