在Java中,您可以将多个Condition
个对象关联到一个ReentrantLock
。 C#等价物是什么?
真实示例: Java Condition
documentation中的示例实现使用两个Condition
对象,notFull
和notEmpty
,与之相关联锁。怎么可能将这个例子翻译成C#?
背景:我经常发现Java代码使用两个Condition
个对象来表示与同一Lock
相关联的各种状态;在C#中,似乎你可以
Monitor.Enter
,然后Monitor.WaitOne
/ Monitor.Pulse
,但这只是一个条件。Auto/ManualResetEvent
个对象,但这些对象在等待后无法以原子方式重新获取给定的锁。 注意:我可以想到一种方法:在单个对象上使用Monitor.WaitOne
/ Monitor.PulseAll
,并在唤醒后检查条件;这就是你用Java做的事情,以防止虚假的唤醒。但这并不是真的,因为它迫使你调用PulseAll
而不是Pulse
,因为Pulse
可能会唤醒一个等待另一个条件的线程。不幸的是,使用PulseAll
代替Pulse
会影响性能(线程竞争同一个锁)。
答案 0 :(得分:1)
我认为如果您正在进行新的开发并且可以执行.NET 4或更高版本,那么新的并发集合类(例如ConcurrentQueue)将为您提供更好的服务。
但是,如果你不能做出这一举动,并严格回答你的问题,在.NET中这有点简化imho,实现一个prod / cons模式,你只需要等待,然后如下脉冲(注意我在记事本上键入此内容)
// max is 1000 items in queue
private int _count = 1000;
private Queue<string> _myQueue = new Queue<string>();
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释放锁定,因此您不会遇到死锁
答案 1 :(得分:0)
@Jason如果队列已满并且您只唤醒一个线程,则无法保证线程是消费者。它可能是一个制作人,你会被卡住。
答案 2 :(得分:0)
我没有遇到过想要在锁中共享状态的C#代码。如果不滚动自己,可以使用SemaphoreSlim
(但我建议使用ConcurrentQueue(T)
或BlockingCollection(T)
)。
public class BoundedBuffer<T>
{
private readonly SemaphoreSlim _locker = new SemaphoreSlim(1,1);
private readonly int _maxCount = 1000;
private readonly Queue<T> _items;
public int Count { get { return _items.Count; } }
public BoundedBuffer()
{
_items = new Queue<T>(_maxCount);
}
public BoundedBuffer(int maxCount)
{
_maxCount = maxCount;
_items = new Queue<T>(_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();
}
}
}
}