views:

40

answers:

3

I have a class that exposes two methods:

 - GetObject

Gets a single object, or returns null if there are none.

 - WaitForObject

Gets a single object, or waits until their is one.

An example implementation:

    

    class MyClass
    {
        MyStack stack;
        public object GetObject()
        {
            return stack.Pop();
        }
        public object WaitForObject()
        {
            object returnValue;
            while (returnValue == null)
                returnValue = stack.Pop()
            return returnValue
        }
    }

With the assumption that MyStack is thread safe, how can I make MyClass thread safe? I.e.

 - GetObject should never block  - Thread doing WaitForObject should get any new objects added to the stack instead of GetObject.

For bonus points, how can users adding objects to the stack notify any listeners that a new object is available? (eliminating the need for polling)

+1  A: 

If MyStack is guaranteed to be thread safe then MyClass is also thread safe. In the two methods you are using only local variables so the methods are reentrant.

Currently users cannot add objects to the stack because the stack field is not visible outside the class. Also I don't see from your code how do listeners will subscribe to any events so that they are notified if an object is added. So you could have a method allowing to add elements to the stack and an event that will be triggered in this case.

Darin Dimitrov
A: 

Polling usually involves some form of sleep - the loop in your example would be a tight loop that would throttle the thead all the time. Add a Thread.Sleep(100) call, or some other sensible value, to poll over time.

The other way to wait would be to register a callback, or have the stack expose a blocking Pop method, either way, these would be implemented in the Stack class in your example.

Bonus Answer: Your class would need to expose an event, when they add an object to the stack, it would fire this event.

class MyClass 
    { 
        MyStack stack; 
        public object GetObject() 
        { 
            return stack.Pop(); 
        } 
        public object WaitForObject() 
        { 
            object returnValue; 
            while (returnValue == null) 
                returnValue = stack.Pop() 
            return returnValue 
        } 
        public void AddObject(object o)
        {
            stack.Push(o);
            OnObjectAdded();
        }
        public event EventHandler ObjectAdded;

        private void OnObjectAdded()
        {
            if (ObjectAdded != null)
                ObjectAdded(this, EventArgs.Empty);
        }
    } 
Adam
Busy waiting with Thread.Sleep is a bad idea. Use a timer or a callback as you mention.
Mikael Svenson
This callback would have to be implemented on the underlying collection (ie, his Stack). Assuming a System.Threading.Timer or a System.Timer, then the tick of the timer would not be guaranteed to run on the same thread. This is not necessarily a problem, but it might not be desirable.
Adam
@Mikael I usually agree, but to keep the polling on the same thread, unfortunately there aren't many options.
Adam
A: 

I think that you can achieve everything with Monitor functionality. just a sketch

class MyClass
{
    private Stack<object> stack = new Stack<object>();
    public object GetObject()
    {
        lock(stack)
        {
            return stack.Count != 0 ? stack.Pop() : null;
        }
    }
    public object WaitForObject()
    {
        lock (stack)
        {
            if (stack.Count == 0)
            {
                // wait until PutObject is called
                Monitor.Wait(stack);
            }

            return stack.Pop();
        }
    }

    public void PutObject(object obj)
    {
        lock (stack)
        {
            stack.Push(obj);
            // notify one thread blocked by WaitForObject call
            Monitor.Pulse(obj);
        }
    }
}
desco