I am using the BlockingQueue code posted in this question, but realized I needed to use a Stack instead of a Queue given how my program runs. I converted it to use a Stack and renamed the class as needed. For performance I removed locking in Push, since my producer code is single threaded.
My problem is how can thread working on the (now) thread safe Stack know when it is empty. Even if I add another thread safe wrapper around Count that locks on the underlying collection like Push and Pop do, I still run into the race condition that access Count and then Pop are not atomic.
Possible solutions as I see them (which is preferred and am I missing any that would work better?):
- Consumer threads catch the InvalidOperationException thrown by Pop().
- Pop() return a nullptr when _stack->Count == 0, however C++-CLI does not have the default() operator ala C#.
- Pop() returns a boolean and uses an output parameter to return the popped element.
Here is the code I am using right now:
generic <typename T>
public ref class ThreadSafeStack
{
public:
ThreadSafeStack()
{
_stack = gcnew Collections::Generic::Stack<T>();
}
public:
void Push(T element)
{
_stack->Push(element);
}
T Pop(void)
{
System::Threading::Monitor::Enter(_stack);
try {
return _stack->Pop();
}
finally {
System::Threading::Monitor::Exit(_stack);
}
}
public:
property int Count {
int get(void)
{
System::Threading::Monitor::Enter(_stack);
try {
return _stack->Count;
}
finally {
System::Threading::Monitor::Exit(_stack);
}
}
}
private:
Collections::Generic::Stack<T> ^_stack;
};