信号量可以小于0吗?我的意思是,假设我有一个 N=3 的信号量,我调用了 4 次“down”,那么 N 将保持为 0,但一个进程会被阻塞?
反之亦然,如果一开始我叫,N能大于3吗?因为正如我所看到的,如果 N 可以高于 3,如果一开始我调用了几次,那么稍后我可以调用比我可以调用的次数更多,从而将更多的进程放在关键部分,然后信号量允许我.
如果有人能为我澄清一下,我将不胜感激。
格雷格
信号量可以小于0吗?我的意思是,假设我有一个 N=3 的信号量,我调用了 4 次“down”,那么 N 将保持为 0,但一个进程会被阻塞?
反之亦然,如果一开始我叫,N能大于3吗?因为正如我所看到的,如果 N 可以高于 3,如果一开始我调用了几次,那么稍后我可以调用比我可以调用的次数更多,从而将更多的进程放在关键部分,然后信号量允许我.
如果有人能为我澄清一下,我将不胜感激。
格雷格
(使用给定 Java 标记的 java.util.concurrent.Semaphore 中的术语。其中一些细节是特定于实现的。我怀疑您的“向下”是 Java 信号量的acquire()
方法,而您的“向上”是release()
。)
是的,您最后一次调用acquire()
将阻塞,直到另一个线程调用release()
或您的线程被中断。
是的,您可以调用release()
更多次,然后再调用更多次 - 至少使用java.util.concurrent.Semaphore
.
信号量的一些其他实现可能有“最大”许可数量的想法,并且超过该最大值的释放调用将失败。JavaSemaphore
类允许相反的情况,信号量可以从负数的许可开始,并且所有acquire()
调用都将失败,直到有足够的release()
调用。一旦许可数量变为非负数,就永远不会再变为负数。
当它为 0 时调用 down 应该不起作用。当它是 3 时调用确实有效。(我在想Java)。
让我再补充一些。许多人认为锁类似于(二进制)信号量(即 - N = 1,因此信号量的值要么是 0(持有)要么是 1(不持有))。但这并不完全正确。锁具有“所有权”的概念,因此它可能是“可重入的”。这意味着持有锁的线程被允许再次调用 lock()(有效地将计数从 0 移动到 -1),因为线程已经持有锁并被允许“重新进入”它。锁也可以是不可重入的。锁持有者应该调用 unlock() 的次数与 lock() 的次数相同。
信号量没有所有权的概念,因此它们不能是可重入的,尽管可以获得尽可能多的许可。这意味着线程在遇到值 0 时需要阻塞,直到有人增加信号量。
此外,在我所看到的(即 Java)中,您可以增加大于 N 的信号量,这也与所有权有关:信号量没有所有权的概念,因此任何人都可以给它更多的许可。与线程不同,只要线程调用 unlock() 而不持有锁,那就是一个错误。(在java中它会抛出一个异常)。
希望这种思考方式有所帮助。
嗨格雷格考虑以下示例:
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
等待还在继续。
所以基本上permit会在每次发布时增加,acquire会减少直到0。一旦达到0,它会等到release在同一个对象上被调用:)
是的,负值意味着您有进程等待释放信号量。正值意味着您可以在信号量阻塞之前多次调用获取。
你可以这样想这个值:一个正数意味着有那么多可用的资源。负值意味着当所有资源都被占用时,有很多实体需要资源。当你获得资源时,你会减少价值,当你释放它时,你会增加价值。如果在减量后该值仍然 >= 0,则您获得资源,否则您的实体将被放入队列中。
维基百科中信号量的一个很好的解释: http://en.wikipedia.org/wiki/Semaphore_(programming)
只需将 N 视为计算有限资源的计数器。由于您不能拥有负数的资源,因此 N 保持 >= 0。如果您的可用资源数量发生变化,则最大 N 也必须更改。在任何其他情况下,我不会认为在不先减少它的情况下增加 n 是一种好的风格。
使用java.util.concurrent.Semaphore
with 方法acquire()
and release()
,我认为 permit 永远是>=0
。假设您要同步线程,以便只有 1 个线程可以在 for 循环中。如果 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 }
但是,您可以从 java 实现 Semaphore 类并创建自己的类来实现这一点。
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和关键字同步。