我正在使用发布的in this question BlockingQueue代码,但我意识到我需要使用堆栈而不是队列,因为我的程序运行方式。我将其转换为使用Stack并根据需要重命名该类。为了性能,我删除了Push中的锁定,因为我的生产者代码是单线程的。
我的问题是如何在(现在)线程安全堆栈上运行线程,知道什么时候它是空的。即使我在Count周围添加另一个线程安全包装器来锁定底层集合,如Push和Pop do,我仍然遇到访问Count然后Pop不是原子的竞争条件。
我看到它们的可能解决方案(这是首选的,我错过任何可以更好的解决方案吗?):
以下是我现在使用的代码:
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;
};
答案 0 :(得分:4)
就个人而言,我会使用您的选项3.,但重命名此TryPop()。
这会使它的行为更像Framework ConcurrentQueue<T>.TryDequeue
(在.NET 4中)。
编辑:
我会这样声明:
public:
bool TryPop([Out] T% result);
在您的实现中,您只需在方法体中设置T值...
答案 1 :(得分:2)
选项#3是可行的方式,Marc Gravell发布了BufferedQueue/BlockingQueue
的优秀实施,他称之为SizeQueue
:
Creating a blocking Queue<T> in .NET?
鉴于Marc的队列示例,在堆栈中进行交换应该非常容易,并且它可以以类似的方式工作。