为什么在C#中没有类似Java的信号量来获取多个许可证?

为什么在C#中没有类似Java的信号量来获取多个许可证?,c#,concurrency,C#,Concurrency,为什么在C#中没有获取信号量多个许可证的方法?我想这样使用我的信号灯: semaphore.Acquire(2); 您可以编写自己的方法到信号量类: namespace Extentions { public static class SemaphoreExtentions { public static void Acquire(this Semaphore semaphore, int permits) { for (

为什么在C#中没有获取信号量多个许可证的方法?我想这样使用我的信号灯:

semaphore.Acquire(2);
您可以编写自己的方法到信号量类:

namespace Extentions
{
    public static class SemaphoreExtentions
    {

        public static void Acquire(this Semaphore semaphore, int permits)
        {
           for (int i = 0; i < permits; ++i)
               semaphore.WaitOne();
        }
    }
}
更新 为避免僵局:

public static void Acquire(this Semaphore semaphore, int permits)
{
    lock (semaphore)
    {
        for (int i = 0; i < permits; ++i)
            semaphore.WaitOne();        
    }
}
publicstaticvoidacquire(此信号量信号量,int允许)
{
锁(信号量)
{
对于(int i=0;i<1;++i)
WaitOne()信号量;
}
}
当然,您不应该在其他地方锁定此信号量,因为它也可能导致死锁。另一个选项是将方法的签名更改为获取(此信号量信号量,int允许,对象同步)并锁定同步对象。

该.Net对象是Win32对象的包装器。由于Win32信号量没有任何一次获取多个资源的机制,因此在.Net Framework中没有简单的方法来提供它


当然,微软有可能在Win32信号量上实现自己的抽象,但这样一来,它们就不能包含在调用中。MS很可能从未考虑过实现这一功能。

好的api设计是一门艺术,真正掌握它的人只有少数。一个重要方面是强调共性。NET信号量类具有这样的特性,您可以使用
WaitOne()
来获取信号量。就像您在所有同步类上所做的那样


而且,通过知道要省略什么,它尤其成为一门艺术。一个定制的
Aquire(n)
方法将在该切割列表中处于较高位置。这是一个非常麻烦的方法,Windows上的底层本机实现也不支持它。这就产生了引发几乎无法调试的死锁的严重风险,代码将被挂在一个不可见的循环方法中,被深埋在CLR中。这为等待添加了大量语义,在需要中止代码时,这些语义非常重要。您可以自行循环,这是一个简单的解决方法,当然在调试死锁时也很明显,您至少可以检查计数器。

如果您只想检查许可证是否可用,而不需要阻塞,则可以以简单安全(无死锁)的方式实现

公共静态bool-Acquire(此信号量信号量,int允许){
对于(int i=0;i<1;i++){
/*
*如果毫秒刺激为零,则该方法不会阻塞。
*它测试等待句柄的状态并立即返回。
*/
如果(!semaphore.WaitOne(0)){
信号量释放(i);
返回false;
}
}
返回true;
}

这可以很容易地修改为包含超时。然而,无限期地包含阻塞将更加困难(它需要信令,否则将导致一个繁忙的外部循环)。

您的问题是如何做到这一点,还是“它为什么不像Java那样工作?”。要得到第一个问题的答案,你需要重新表述你的问题。你不会得到第二个问题的答案。事实上,我可以对第二个问题做一个很好的猜测:“因为C#开发团队对此进行了长期而艰苦的思考,并且找到了一些很好的理由不使用Java。”。或者是或多或少相当但稍微不友好的“因为它可能是那些没有经过深思熟虑的Java特性之一,每个人都只是出于习惯而使用它”。@CompuChip:实际上可能两者都不是。C#开发团队甚至没有实现库函数。而且该功能的使用频率可能不足以保证.Net团队实现所需的工作。Good point@Gabe+您应该锁定整个获取方法体(考虑两个线程同时获取许可证)。这没关系,但我真的很想知道为什么C#中没有开箱即用的计数信号量,这会导致死锁。在您的示例中,如果您有两个客户,并且两个客户都试图同时获得6个,会发生什么情况?假设他们都获得了5,那么他们都会等待对方放弃最后的1。我认为信号量的所有者对象必须担心其成员的线程安全。@Gabe你是关心我的答案还是Mixer的commet?
lock(信号量)
不是个好主意。需要一个仅由试图获取给定信号量的调用方锁定的锁对象,例如,
readonly object acquireLock=new object()<代码>锁(acquireLock).
原因:
Acquire
可能持有此锁很长时间,因为它积累了足够的许可证。假设其他地方的代码希望
锁定(信号量)
用于其他目的-它将被卡住等待此代码。不幸的是,传入一个
sync
对象可能会导致相反的问题:两个不同的调用者,对同一信号量使用不同的sync对象,可能会死锁。。。他们本可以将其添加到SemaphoreSlim中,但似乎他们主要为其保留了Semaphore类的API。
public static void Acquire(this Semaphore semaphore, int permits)
{
    lock (semaphore)
    {
        for (int i = 0; i < permits; ++i)
            semaphore.WaitOne();        
    }
}
public static bool Acquire (this Semaphore semaphore, int permits) {
    for (int i = 0; i < permits; i++) {
        /*
         * If millisecondsTimeout is zero, the method does not block.
         * It tests the state of the wait handle and returns immediately.
         */
        if (!semaphore.WaitOne (0)) {
            semaphore.Release (i);
            return false;
        }
    }
    return true;
}