我正在尝试围绕堆栈设计一个数据结构,该数据结构会阻塞,直到堆栈中有可用的项目为止。我尝试使用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();
        }
    }
}

我的假设是当AutoResetEvent函数调用通过时,所有等待线程的WaitOne()重置。但是,似乎有多个线程正在进入。除非我弄乱了某个地方的逻辑。

编辑:这是针对Silverlight的。

最佳答案

除非您只是想了解线程的工作方式,否则最好使用阻塞集合。这将为您提供由堆栈支持的阻塞收集:

ConcurrentStack<SomeType> MyStack = new ConcurrentStack<SomeType>();
BlockingCollection<SomeType> SharedStack = new BlockingCollection<SomeType>(MyStack)

然后,您可以以线程安全的方式访问它,并为您正确完成所有阻止。参见here

您可以通过调用sharedStack.Take()来使用sharedStack,然后它会阻止获取,直到从堆栈中获取某些内容为止。

编辑:
花了我一段时间(两次尝试),但是我认为我已经解决了您的问题。

考虑一个有3个线程等待事件的空堆栈。

调用Add,堆栈具有一个对象,并且该事件允许一个线程。

立即添加再次被调用。

现在,第一个线程等待从Add中获取锁。

Add将第二个对象添加到堆栈中,并让另一个线程通过该事件。

现在,两个对象在堆栈上,并且有2个线程通过该事件,都在等待锁。

现在,“首先获取”线程需要锁定并弹出。看到堆栈上的一个对象静止不动,然后按CALLS SET。

通过事件允许使用第三个线程。

第二个Get线程现在需要锁定并弹出。在堆栈中看不到任何内容,并且不调用set。

但。太晚了。第三线程已经被允许通过,因此当第二线程放弃该锁时,第三线程尝试从空堆栈中弹出并抛出。

10-05 22:52