AutoResetEvent和多个集合

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.

Rus*_*est 6

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

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.

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


soh*_*hum 1

我没有验证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)