如何使 Stack.Pop 线程安全
Posted
技术标签:
【中文标题】如何使 Stack.Pop 线程安全【英文标题】:How to make Stack.Pop threadsafe 【发布时间】:2011-01-27 18:45:41 【问题描述】:我正在使用 in this question 发布的 BlockingQueue 代码,但我意识到鉴于我的程序运行方式,我需要使用堆栈而不是队列。我将其转换为使用 Stack 并根据需要重命名该类。为了提高性能,我删除了 Push 中的锁定,因为我的生产者代码是单线程的。
我的问题是(现在)线程安全堆栈上的线程如何知道它何时为空。即使我在 Count 周围添加另一个线程安全包装器,像 Push 和 Pop 那样锁定底层集合,我仍然会遇到访问 Count 然后 Pop 不是原子的竞争条件。
我看到的可能的解决方案(这是首选,我错过了任何更好的解决方案吗?):
-
消费线程捕获由 Pop() 抛出的 InvalidOperationException。
当 _stack->Count == 0 时,Pop() 返回一个 nullptr,但是 C++-CLI 没有 default() 运算符 ala C#。
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;
;
【问题讨论】:
"为了提高性能,我删除了 Push 中的锁定,因为我的生产者代码是单线程的。"你这是什么意思?你只有一个制作人?您的生产者代码是否与消费者同时运行? 是的,我只有一个生产者,而且我的生产者代码不会与消费者同时运行。它先运行,然后使用生产者生成的 ThreadSafeStack 运行多个消费者。 【参考方案1】:就个人而言,我会使用您的选项 3.,但重命名此 TryPop()。
这将使它的行为更像框架的 ConcurrentQueue<T>.TryDequeue
(在 .NET 4 中)。
编辑:
我会这样声明:
public:
bool TryPop([Out] T% result);
在您的实现中,您只需在方法体中设置 T 值...
【讨论】:
完全不熟悉c++-cli中如何使用out参数,有链接或示例代码吗? 鉴于如果堆栈为空,TryPop 应该返回一个 nullptr,您是否建议在我调用 TryPop 的地方传入一个 nullptr 或确保我将 out 参数设置为一个 nullptr?同样,我不确定如何执行与 default(T) 等效的 c#。我发现了这个:***.com/questions/1962600/… @user260197:我的 C++/CLI 有点生疏,但我认为你只需这样做:result = T();【参考方案2】:选项 #3 是可行的方法,Marc Gravell 发布了一个出色的 BufferedQueue/BlockingQueue
实现,他称之为SizeQueue
:
Creating a blocking Queue<T> in .NET?
鉴于 Marc 的队列示例,在堆栈中交换应该很容易,并且可以以类似的方式工作。
【讨论】:
以上是关于如何使 Stack.Pop 线程安全的主要内容,如果未能解决你的问题,请参考以下文章
如何使 ObservableCollection 线程安全?