我正在尝试围绕堆栈设计数据结构,直到堆栈有可用项目为止.我尝试使用AutoResetEvent,但我想我误解了同步过程是如何工作的.基本上,看下面的代码,我试图从没有可用的堆栈弹出.
似乎AutoResetEvent的行为类似于信号量.那是对的吗?我可以在BlockingStack.Get()中删除Set()并完成它吗?或者这会导致我只使用我的一个堆栈项目.
public class BlockingStack { private Stack<MyType> _internalStack; private AutoResetEvent _blockUntilAvailable; public BlockingStack() { _internalStack = new Stack<MyType>(5); _blockUntilAvailable = new AutoResetEvent(false); for (int i = 0; i < 5; ++i) { var obj = new MyType(); Add(obj); } } public MyType Get() { _blockUntilAvailable.WatiOne(); lock (_internalStack) { var obj = _internalStack.Pop(); if (_internalStack.Count > 0) { _blockUntilAvailable.Set(); // do I need to do this? } return obj; } } public void Add(MyType obj) { lock (_internalStack) { _internalStack.Push(obj); _blockUntilAvailable.Set(); } } }
我的假设是当一个人通过WaitOne()函数调用时,AutoResetEvent会重置所有等待的线程.然而,似乎有多个线程进入.除非我在某处弄乱了我的逻辑.
编辑:这是为Silverlight.
解决方法
除非您只是想了解线程的工作原理,否则最好使用阻塞集合.这将为您提供由堆栈支持的阻塞集合:
ConcurrentStack<SomeType> MyStack = new ConcurrentStack<SomeType>(); BlockingCollection<SomeType> SharedStack = new BlockingCollection<SomeType>(MyStack)
然后,您可以以线程安全的方式访问它,并为您完成所有阻塞.见here
你可以通过调用sharedStack.Take()来使用sharedStack,然后它会阻塞,直到从栈中取出一些东西.
编辑:
花了一段时间(和两次尝试),但我认为我已经解决了你的问题.
考虑一个空堆栈,其中有3个线程在等待事件.
调用Add,堆栈有一个对象,允许一个线程通过事件.
第一个线程现在等待从Add获取锁定.
Add向堆栈添加第二个对象,并让另一个线程通过该事件.
现在堆栈上有两个对象,事件中有两个线程,两个都在等待锁定.
First Get线程现在需要锁定和弹出.在堆栈上看到一个对象仍然是CALLS SET.
第三个线程允许通过事件.
第二个Get线程现在需要锁定和弹出.在堆栈中看不到任何内容,也不会调用set.
但.太晚了.第三个线程已被允许通过,因此当第二个线程放弃锁定时,第三个线程会尝试从空堆栈弹出并抛出.