简体   繁体   English

ConcurrentStack:如何检查是否包含对象?

[英]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>.ContainsConcurrentStack<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: 所以答案取决于您的要求:

  • If you want to check value sometimes (eg complexity is does not matter) - just use _stack.FirstOrDefault(v=> v==valueWhichYouTryToFind) . 如果你想有时检查值(例如复杂性无关紧要) - 只需使用_stack.FirstOrDefault(v=> v==valueWhichYouTryToFind) This can be slow, because you have to iterate all elements. 这可能很慢,因为您必须迭代所有元素。 However task will be solved. 但任务将得到解决。 And please note, that stack can be changed before this function will be finished (eg this function can return true after stack pop operation will be finished) 请注意,在完成此功能之前可以更改该堆栈(例如,此功能在stack pop操作完成后可以返回true
  • If you want to often check values inside huge stack - then you have to create complex structure with concurrent set + stack. 如果你想经常检查巨大堆栈中的值 - 那么你必须创建具有并发set + stack的复杂结构。 I recommend (this is just my opinion) to use just simple Stack + HashSet + locks, because it is quite simpler. 我建议(这只是我的观点)使用简单的Stack + HashSet +锁,因为它非常简单。

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. 请注意, ConcurrentStackConcurrentQueueConcurrentBag是无锁实现的。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM