在等待之前释放锁定,然后重新获取锁定
在Java中,您可以将多个Condition
对象关联到单个ReentrantLock
。 C#等价物是什么?
实际示例: Java Condition
文档中的示例实现使用绑定到同一锁的两个Condition
对象notFull
和notFull
。 怎么可能将这个例子翻译成C#?
背景 :我经常发现Java代码使用两个Condition
对象来表示与同一个Lock
相关联的各种状态; 在C#中,似乎你也可以
- 调用
Monitor.Enter
一个对象,然后Monitor.WaitOne
/Monitor.Pulse
,但这只是一个条件。 - 使用多个
Auto/ManualResetEvent
对象,但这些对象在等待后无法以primefaces方式重新获取给定的锁。
注意 :我可以想到一种方法:在单个对象上使用Monitor.WaitOne
/ Monitor.PulseAll
,并在唤醒后检查条件; 这就是你用Java做的事情,以防止虚假的唤醒。 但它并没有真正做到,因为它会强制你调用PulseAll
而不是Pulse
,因为Pulse
可能会唤醒一个等待另一个条件的线程。 不幸的是,使用PulseAll
而不是Pulse
会影响性能(线程竞争相同的锁)。
我认为如果您正在进行新的开发并且可以执行.NET 4或更高版本,那么新的并发集合类(如ConcurrentQueue)将为您提供更好的服务。
但是,如果你不能做出这一举动,并严格回答你的问题,在.NET中这有点简化imho,实现一个prod / cons模式,你只需要等待然后如下脉冲(注意我输入了这个记事本)
// max is 1000 items in queue private int _count = 1000; private Queue _myQueue = new Queue (); private static object _door = new object(); public void AddItem(string someItem) { lock (_door) { while (_myQueue.Count == _count) { // reached max item, let's wait 'till there is room Monitor.Wait(_door); } _myQueue.Enqueue(someItem); // signal so if there are therads waiting for items to be inserted are waken up // one at a time, so they don't try to dequeue items that are not there Monitor.Pulse(_door); } } public string RemoveItem() { string item = null; lock (_door) { while (_myQueue.Count == 0) { // no items in queue, wait 'till there are items Monitor.Wait(_door); } item = _myQueue.Dequeue(); // signal we've taken something out // so if there are threads waiting, will be waken up one at a time so we don't overfill our queue Monitor.Pulse(_door); } return item; }
更新:要清除任何混淆,请注意Monitor.Wait会释放锁定,因此您不会遇到死锁
@Jason如果队列已满并且您只唤醒一个线程,则无法保证线程是消费者。 它可能是一个制作人,你会陷入困境。
我没有遇到过想要在锁中共享状态的C#代码。 如果不滚动自己,可以使用SemaphoreSlim
(但我推荐使用ConcurrentQueue(T)
或BlockingCollection(T)
)。
public class BoundedBuffer { private readonly SemaphoreSlim _locker = new SemaphoreSlim(1,1); private readonly int _maxCount = 1000; private readonly Queue _items; public int Count { get { return _items.Count; } } public BoundedBuffer() { _items = new Queue (_maxCount); } public BoundedBuffer(int maxCount) { _maxCount = maxCount; _items = new Queue (_maxCount); } public void Put(T item, CancellationToken token) { _locker.Wait(token); try { while(_maxCount == _items.Count) { _locker.Release(); Thread.SpinWait(1000); _locker.Wait(token); } _items.Enqueue(item); } catch(OperationCanceledException) { try { _locker.Release(); } catch(SemaphoreFullException) { } throw; } finally { if(!token.IsCancellationRequested) { _locker.Release(); } } } public T Take(CancellationToken token) { _locker.Wait(token); try { while(0 == _items.Count) { _locker.Release(); Thread.SpinWait(1000); _locker.Wait(token); } return _items.Dequeue(); } catch(OperationCanceledException) { try { _locker.Release(); } catch(SemaphoreFullException) { } throw; } finally { if(!token.IsCancellationRequested) { _locker.Release(); } } } }