[英]ConcurrentStack: How to check if contains object?
How can i check if it contains specific object in the stack ? 如何检查它是否包含堆栈中的特定对象?
private ConcurrentStack<int> cs = new ConcurrentStack<int>();
cs.Push(1);
The method Stack<T>.Contains
isn't available in the ConcurrentStack<T>
-class . 方法
Stack<T>.Contains
在ConcurrentStack<T>
类中不可用。 I guess because that would not be thread-safe. 我想因为那不是线程安全的。
So if you needed it you had to use a lock, then you could use Enumerable.Contains
: 因此,如果您需要它,您必须使用锁,然后您可以使用
Enumerable.Contains
:
private ConcurrentStack<int> cs = new ConcurrentStack<int>();
private Object csLockObject = new Object();
... ...
bool contains = false;
lock (csLockObject)
{
contains = cs.Contains(1);
}
But while you enumerate this snapshot it's possible that another thread adds or removes items to/from the stack. 但是,当您枚举此快照时,另一个线程可能会向堆栈添加项目或从堆栈中删除项目。 If you wanted to prevent that you also need a lock where you add/remove.
如果您想要防止在添加/删除的位置还需要锁定。
I want to avoid duplicates
我想避免重复
Well, you could use a class like this which uses a ConcurrentDictionary
to check if it's unique: 好吧,你可以使用这样的类,它使用
ConcurrentDictionary
来检查它是否是唯一的:
public class ConcurrentUniqueStack<T>
{
private readonly ConcurrentDictionary<T, int> _itemUnique; // there is no ConcurrentHashSet so we need to use a Key-only dictionary
private readonly ConcurrentStack<T> _stack;
public ConcurrentUniqueStack() : this(EqualityComparer<T>.Default)
{
}
public ConcurrentUniqueStack(IEqualityComparer<T> comparer)
{
_stack = new ConcurrentStack<T>();
_itemUnique = new ConcurrentDictionary<T, int>(comparer);
}
public bool TryPush(T item)
{
bool unique = _itemUnique.TryAdd(item, 1);
if (unique)
{
_stack.Push(item);
}
return unique;
}
public bool TryPop(out T result)
{
bool couldBeRemoved = _stack.TryPop(out result);
if (couldBeRemoved)
{
_itemUnique.TryRemove(result, out int whatever);
}
return couldBeRemoved;
}
public bool TryPeek(out T result) => _stack.TryPeek(out result);
}
This structure is not optimized for this operation. 此结构未针对此操作进行优化。
It means, that value lookup operation has O(N) complexity, because you have to iterate whole collection. 这意味着,值查找操作具有O(N)复杂性,因为您必须迭代整个集合。
So answer depends of your requirements: 所以答案取决于您的要求:
_stack.FirstOrDefault(v=> v==valueWhichYouTryToFind)
. _stack.FirstOrDefault(v=> v==valueWhichYouTryToFind)
。 This can be slow, because you have to iterate all elements. true
after stack pop
operation will be finished) stack pop
操作完成后可以返回true
) Your problem has no easy answer. 你的问题没有简单的答案。 The reason is that
ConcurrentStack
is designed to be used in a multi-threaded environment, which comes with some caveats. 原因是
ConcurrentStack
被设计用于多线程环境,这带来一些警告。 The key issue is that your Contains
operation has to be atomic. 关键问题是您的
Contains
操作必须是原子的。 Let's explore the options you have: 让我们探索您的选择:
1. Enumerate the stack 1.枚举堆栈
You could enumerate the stack with foreach
and check if your value is in there. 您可以使用
foreach
枚举堆栈并检查您的值是否在那里。 The problem is that this enumeration is not atomic and in a multi-threaded environment even dangerous, because a change of the stack from another thread within the enumeration will cause an InvalidOperationException
. 问题是这个枚举不是原子的,在多线程环境中甚至是危险的,因为从枚举中的另一个线程更改堆栈将导致
InvalidOperationException
。 The LINQ method Enumerable.Contains
makes no difference here, because it will also enumerate the stack. LINQ方法
Enumerable.Contains
在这里没有区别,因为它也会枚举堆栈。
You could simply place your enumeration inside a lock
, which only works if all accesses to the stack (ie pushes and pops) are within the same lock. 您可以简单地将枚举放在一个
lock
,只有当对堆栈的所有访问(即推送和弹出)都在同一个lock
中时才会起作用。 And then you don't need a ConcurrentStack
. 然后你不需要
ConcurrentStack
。 You can simply go with a regular Stack
. 您可以简单地使用常规
Stack
。 Note that ConcurrentStack
, ConcurrentQueue
and ConcurrentBag
are implemented lock-free. 请注意,
ConcurrentStack
, ConcurrentQueue
和ConcurrentBag
是无锁实现的。 Placing them inside a lock negates this. 把它们放在一个锁内会否定这一点。
2. Peek the stack top 2.看看堆栈顶部
You can call TryPeek
on the stack, which is is an atomic operation. 你可以在堆栈上调用
TryPeek
,这是一个原子操作。 The problem is that you will only get the value on the stack top. 问题是你只能获得堆栈顶部的值。 If this is fine for you, you can go with this solution.
如果这对您没问题,您可以使用此解决方案。 Otherwise you can't.
否则你不能。
3. Pop and push the values 3.弹出并推送值
You could TryPopRange
all the values from the stack and then TryPushRange
the values back onto the stack. 你可以
TryPopRange
堆栈中的所有值,然后TryPushRange
值回到堆栈。 I've added this option merely for the sake of completeness, since it actually is totally crazy. 我添加此选项仅仅是为了完整性,因为它实际上是完全疯了。 When you pop all the values off the stack, they will not be there anymore until you push them back.
当你从堆栈中弹出所有值时,它们将不再存在,直到你将它们推回去。 If anyone wants to pop the top of the stack they are in bad luck.
如果有人想要弹出堆栈的顶部,他们运气不好。 Even worse, when they push another value, you don't only miss it in your check, it will also shake up the stack order.
更糟糕的是,当他们推出另一个值时,你不仅会在支票中错过它,还会改变堆栈顺序。
This all means that you again have to place the whole operation inside a lock, which you can have easier with option 1 (enumerating the stack). 这意味着你必须再次将整个操作放在一个锁中,使用选项1(枚举堆栈)可以更容易。
Conclusion 结论
If you need to only check the stack top, use TryPeek
. 如果您只需要检查堆栈顶部,请使用
TryPeek
。 If you need to check all values, use a Stack
and place it inside a lock whenever you access it. 如果需要检查所有值,请使用
Stack
并在访问时将其置于锁定内。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.