soh*_*hum 6 c# concurrency stack autoresetevent data-structures
我正在尝试围绕堆栈设计数据结构,直到堆栈有可用项目为止.我尝试使用AutoResetEvent但我认为我误解了同步过程的工作原理.基本上,看下面的代码,我试图从没有可用的堆栈弹出.
它似乎AutoResetEvent表现得像一个信号量.那是对的吗?可我只是摆脱了Set()中BlockingStack.Get(),并用它做什么?或者这会导致我只使用我的一个堆栈项目.
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的假设是AutoResetEvent当一个人通过WaitOne()函数调用时重置所有等待的线程.然而,似乎有多个线程进入.除非我在某处弄乱了我的逻辑.
编辑:这是为Silverlight.
除非您只是想了解线程的工作原理,否则最好使用阻塞集合.这将为您提供由堆栈支持的阻塞集合:
ConcurrentStack<SomeType> MyStack = new ConcurrentStack<SomeType>();
BlockingCollection<SomeType> SharedStack = new BlockingCollection<SomeType>(MyStack)
Run Code Online (Sandbox Code Playgroud)
然后,您可以以线程安全的方式访问它,并为您完成所有阻塞.看到这里
您可以通过调用来使用sharedStack,sharedStack.Take()然后阻止它,直到从堆栈中取出某些内容.
编辑:我花了一段时间(和两次尝试),但我认为我已经解决了你的问题.
考虑一个空堆栈,其中有3个线程在等待事件.
调用Add,堆栈有一个对象,允许一个线程通过事件.
立即添加再次调用.
第一个线程现在等待从Add获取锁定.
Add向堆栈添加第二个对象,并让另一个线程通过该事件.
现在堆栈上有两个对象,事件中有两个线程,两个都在等待锁定.
First Get线程现在需要锁定和弹出.在堆栈上看到一个对象仍然是CALLS SET.
第三个线程允许通过事件.
第二个Get线程现在需要锁定和弹出.在堆栈中看不到任何内容,也不会调用set.
但.太晚了.第三个线程已被允许通过,因此当第二个线程放弃锁定时,第三个线程会尝试从空堆栈弹出并抛出.
我没有验证Monitor基于信号量的解决方案,但我确实编写了一个似乎有效的基于信号量的解决方案:
public class Semaphore
{
private int _count;
private int _maximum;
private object _countGuard;
public Semaphore(int maximum)
{
_count = 0;
_maximum = maximum;
_countGuard = new object();
}
public void WaitOne()
{
while (true)
{
lock (_countGuard)
{
if (_count < _maximum)
{
_count++;
return;
}
}
Thread.Sleep(50);
}
}
public void ReleaseOne()
{
lock (_countGuard)
{
if (_count > 0)
{
_count--;
}
}
}
}
public class BlockingStack
{
private Stack<MyType> _internalStack;
private Semaphore _blockUntilAvailable;
public BlockingStack()
{
_internalStack = new Stack<MyType>(5);
_blockUntilAvailable = new Semaphore(5);
for (int i = 0; i < 5; ++i)
{
var obj = new MyType();
Add(obj);
}
}
public MyType Get()
{
_blockUntilAvailable.WaitOne();
lock (_internalStack)
{
var obj = _internalStack.Pop();
return obj;
}
}
public void Add(MyType obj)
{
lock (_internalStack)
{
_internalStack.Push(obj);
_blockUntilAvailable.ReleaseOne();
}
}
}
Run Code Online (Sandbox Code Playgroud)