Search code examples
c#data-structuresconcurrencystackautoresetevent

AutoResetEvent and multiple Sets


I'm trying to design a data-structure around a stack that blocks until the stack has an item available. I tried using an AutoResetEvent but I think I misunderstood how that synchronization process works. Basically, looking at the following code, I am trying to Pop from stack when there's nothing available.

It seems that the AutoResetEvent is behaving like a semaphore. Is that correct? Can I just get rid of the Set() in BlockingStack.Get() and be done with it? Or will that result in a situation where I'm only using one of my stack items.

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();
        }
    }
}

My assumption was the AutoResetEvent resets for all waiting threads when one gets through on the WaitOne() function call. However, it seems that multiple threads are getting in. Unless I've messed up my logic somewhere.

EDIT: This is for Silverlight.


Solution

  • I did not verify the Monitor based solution, but I did write a semaphore-based solution that appears to be working:

    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();
            }
        }
    }