信号量-初始计数有什么用?


91

http://msdn.microsoft.com/zh-CN/library/system.threading.semaphoreslim.aspx

要创建信号灯,我需要提供一个初始计数和最大计数。MSDN指出,初始计数为-

可以同时授予的信号量请求的初始数量。

虽然它指出最大数量是

可以同时授予的信号量请求的最大数量。

我可以理解,最大数量是可以同时访问资源的最大线程数。但是,初始计数的用途是什么?

如果我创建的信号量的初始计数为0,最大计数为2,则我的线程池线程都无法访问该资源。如果将初始计数设置为1,最大计数设置为2,则只有线程池线程可以访问资源。仅当我将初始计数和最大计数都设置为2时,两个线程才能够同时访问资源。那么,我真的对初始计数的重要性感到困惑吗?

SemaphoreSlim semaphoreSlim = new SemaphoreSlim(0, 2); //all threadpool threads wait
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 2);//only one thread has access to the resource at a time
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(2, 2);//two threadpool threads can access the resource concurrently

7
您怎么从未接受SVGreg的答案?
约翰·

Answers:


79

是的,当初始数字设置为0时,在增加“ CurrentCount”属性时,所有线程都将等待。您可以使用Release()或Release(Int32)来实现。

Release(...)-将增加信号量计数器

等待(...)-将其递减

您不能将计数器(“ CurrentCount”属性)的增量增加到大于初始化时设置的最大计数。

例如:

SemaphoreSlim^ s = gcnew SemaphoreSlim(0,2); //s->CurrentCount = 0
s->Release(2); //s->CurrentCount = 2
...

s->Wait(); //Ok. s->CurrentCount = 1
...

s->Wait(); //Ok. s->CurrentCount = 0
...

s->Wait(); //Will be blocked until any of the threads calls Release()

1
您的代码将更好地显示在答案中,而不是作为注释。
克里斯·弗雷德

14
大声笑,这可能是我第五次收到相同的答案,因为构造函数的文档总是使我困惑于要设置的值。干杯
BlueStrat '16

67

那么,我真的对初始计数的重要性感到困惑吗?

在这里可能Wait有用的一个重要点是减少信号量计数并Release增加它。

initialCount是将立即允许的资源访问次数。换句话说,就是在Wait实例化信号量后可以立即调用而不阻塞的次数。

maximumCount是信号量可以获得的最高计数。这是Release假设initialCountcount为零时可以在不引发异常的情况下调用的次数。如果initialCount将设置为与相同的值,maximumCountRelease在实例化信号量后立即调用将引发异常。


20
这很有帮助!我一直在向后考虑信号量,因为initialCount是初始BLOCKED资源的数量,而不是立即可用的资源数量。谢谢。
菲利普·田纳2014年

5
@PhilipTenn,我同意-关于这方面的文档尚不明确
BlueStrat

我同意,他们应该更改该变量名称或更新文档
IronHide

@Sandbox您应该接受IMO的答案,因为它确实说明了initialCount参数的含义。
米切尔·图尔琴(MichałTurczyn),

8

您希望一次能够访问多少个线程?将您的初始计数设置为该数字。如果该数字在程序生命周期中永远不会增加,请将最大计数也设置为该数字。这样,如果释放资源时出现编程错误,则程序将崩溃并通知您。

(有两种构造方法:一种仅接受初始值,另一种另外采用最大计数。请选择适当的一种。)


1

这样,当当前线程创建信号时,它可能从一开始就要求一些资源。


因此,您的意思是当我希望两个工作线程访问资源时,应该更改初始计数吗?
沙盒

否。当前线程要求计数。如果您不希望当前线程声明任何访问权限0或将重载与一个参数一起使用。
Erno

1

如果您希望一段时间内没有线程可以访问您的资源,则将初始计数传递为0,并且当您希望在创建信号量后才授予所有访问权限时,您传递的初始计数值应等于最大计数。例如:

hSemaphore = CreateSemaphoreA(NULL, 0, MAX_COUNT, NULL) ;

//Do something here
//No threads can access your resource

ReleaseSemaphore(hSemaphore, MAX_COUNT, 0) ;

//All threads can access the resource now

正如MSDN文档中引用的那样:“ ReleaseSemaphore的另一种用法是在应用程序初始化期间。应用程序可以创建一个初始计数为零的信号灯。这会将信号灯的状态设置为无信号,并阻止所有线程访问受保护的资源。完成初始化后,它使用ReleaseSemaphore将计数增加到最大值,以允许正常访问受保护的资源。”


抱歉,我给了您C ++的示例,尽管可以消除疑问。
Abhineet

0

信号量可用于保护资源池。我们使用资源池来重用创建起来昂贵的东西-例如数据库连接。

因此,初始计数是指某个过程开始时池中可用资源的数量。当您阅读initialCountin代码时,您应该考虑创建此资源池需要付出多少努力。

我真的对初始计数的重要性感到困惑吗?

Initial count = Upfront cost

因此,根据应用程序的使用情况,此值可能会对应用程序的性能产生巨大影响。这不仅仅是一些任意数字。

您应该仔细考虑所创建的内容,创建这些内容的成本以及立即需要多少。从字面上看,您应该能够绘制出此参数的最佳值,并且应该考虑使其可配置,以便可以使过程的性能适应执行时间。


-1

正如MSDN在“备注”部分中解释的那样:

如果initialCount小于maximumCount,则效果与当前线程调用WaitOne(maximumCount减去initialCount)时间相同。如果您不想为创建信号量的线程保留任何条目,请对maximumCount和initialCount使用相同的数字。

因此,如果初始计数为0,最大值为2,则好像主线程调用了WaitOne两次,因此我们已达到容量(信号量计数现在为0),并且没有线程可以进入信号量。同样,如果初始计数为1,最大为2,则一次调用WaitOnce,并且只有一个线程可以进入,然后我们才能再次达到容量,依此类推。

如果将0用作初始计数,我们总是可以调用Release(2)将信号计数增加到最大,以允许最大数量的线程获取资源。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.