编辑:我甚至想提出这个问题暂时疯狂,但当时有意义(参见下面的编辑2)。
对于.NET 3.5项目,我需要检查两种类型的资源( R1 和 R2 )。每种资源类型可以随时(例如)有10个实例。
当其中一种资源可用时,我的工作线程需要唤醒(存在可变数量的线程)。在早期的实现中,只有一种资源类型,我使用信号量来检查可用性。
现在我需要等待两个单独的信号量( S1 和 S2 )来跟踪资源的可用性。
WaitHandle[] waitHandles = new WaitHandle[] { s1, s2 };
int signalledHandle = WaitHandle.WaitAny(waitHandles);
switch (signalledHandle)
{
case 0:
// Do stuff
s1.Release();
case 1:
// Do stuff
s2.Release();
}
然而,这有一个问题。来自WaitAny
上的MSDN文档:
如果有多个对象变为 在通话期间发出信号,返回 value是数组的索引 最小的信号对象 所有信号的索引值 对象。
这表明在调用WaitAny
之后,我的信号量计数可能会减少1。因为signalledHandle
将指示s1已发出信号,我将开始使用资源 R1 ,并在完成后释放它。但是,由于我不知道 S2 是否已发出信号,因此此资源的可用性计数现在可能已关闭。如果发生这种情况10次,我的信号量将永久“空”,资源 R2 将不再使用。
处理此问题的最佳方法是什么?我是否应该从使用两个信号量切换到简单计数器,并在任何一个计数器更改时发出AutoResetEvent信号?我错过了一些更优雅的方法吗?
编辑1:
根据Ravadre的说法,在WaitAny
之后,只有一个信号量会被改变。略微修改他的例子似乎证实了这一点,但有没有人可以指出我指出这个的一些官方文档?
编辑2:
我在回家的路上想着这个。只有这样,我才意识到WaitAny
必须有用。这个问题不仅限于信号量,而是几乎任何类型的同步对象,使WaitAny
几乎没用。
答案 0 :(得分:5)
如果我理解你的问题,我认为你的解决方案是完全正常的,而你只是在解释msdn引用。调用WaitHandle.WaitAny()
时,您将得到最低的索引,但是您将只锁定一个waitHandle(在这种情况下是信号量),请检查以下示例代码:
Semaphore s1 = new Semaphore(1, 2);
Semaphore s2 = new Semaphore(1, 2);
WaitHandle[] handles = new WaitHandle[] { s1, s2 };
int x = WaitHandle.WaitAny(handles);
int prevS1 = s1.Release();
int prevS2 = s2.Release();
在这种情况下,prevS1
将等于0,因为信号量s1
“等待”,因此它的计数器已减少为0,而prevS2
将等于1,因为它的状态没有改变,因为它的实例化(Release()
方法在释放之前返回计数器,所以返回1表示“它是1,现在它是2”)。
您可能希望查看的另一个资源:http://www.albahari.com/threading/part2.aspx#_Wait_Handles。虽然它不是“官方”来源,但我认为没有理由认为它不可靠。
答案 1 :(得分:1)
出于您的目的,在调用WaitHandle.WaitAny()
方法时,结果无关紧要。重要的是一个WaitHandle被发出信号,所以你需要再次尝试获取锁定/同步。
void Main() {
var semaphoreOne = new SemaphoreSlim(0, 1);
var semaphoreTwo = new SemaphoreSlim(0, 1);
ReleaseSemaphoreAfterWhile(semaphoreOne);
bool firstAccepted;
bool secondAccepted = false;
while ((firstAccepted = semaphoreOne.Wait(0)) == false &&
(secondAccepted = semaphoreTwo.Wait(0)) == false) {
var waitHandles = new [] {
semaphoreOne.AvailableWaitHandle, semaphoreTwo.AvailableWaitHandle
};
WaitHandle.WaitAny(waitHandles);
Console.WriteLine("SemaphoreOne Before Lock = " + semaphoreOne.CurrentCount);
Console.WriteLine("SemaphoreTwo Before Lock = " + semaphoreTwo.CurrentCount);
}
if (firstAccepted) {
Console.WriteLine("semaphore 1 was locked");
} else if (secondAccepted) {
Console.WriteLine("semaphore 2 was locked");
} else {
throw new InvalidOperationException("no semaphores were signaled");
}
}
Random rd = new Random();
public void ReleaseSemaphoreAfterWhile(SemaphoreSlim semaphore) {
var sleepWork =(int)rd.Next(100, 1000);
ThreadPool.QueueUserWorkItem(t => {
Thread.Sleep(10000 + sleepWork);
semaphore.Release();
});
}
其他实现有相同的想法/逻辑的空间,但是使用while循环,你保证只获得一个信号量,如果没有空间,它会锁定线程,直到任何WaitHandle得到信号 - 考虑SemaphoreSlim
实例.Release()
方法。
不幸的是(正如评论中所指出的那样)他们对网络中的线程同步存在一些误解,但上面的代码可以帮助您解决问题。