在什么情况下 TryDequeue 和类似的 System.Collections.Concurrent 收集方法会失败

发布于 2024-10-31 15:37:37 字数 292 浏览 1 评论 0原文

我最近注意到 System.Collections.Concurrent 命名空间通常会看到 Collection.TrySomeAction() 而不是 Collection.SomeAction()

这是什么原因呢?我认为这与锁定有关?

所以我想知道在什么条件下尝试(例如)从堆栈、队列、包等中出列项目会失败?

I have recently noticed that inside the collection objects contained in System.Collections.Concurrent namespace it is common to see Collection.TrySomeAction() rather then Collection.SomeAction().

What is the cause of this? I assume it has something to do with locking?

So I am wondering under what conditions could an attempt to (for example) Dequeue an item from a stack, queue, bag etc.. fail?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦幻的心爱 2024-11-07 15:37:37

System.Collections.Concurrent 命名空间中的集合被认为是线程安全的,因此可以使用它们编写在线程之间共享数据的多线程程序。

在 .NET 4 之前,如果多个线程可能访问单个共享集合,则必须提供自己的同步机制。每次修改集合的元素时都必须锁定该集合。您可能还需要在每次访问(或枚举)集合时锁定该集合。这是最简单的多线程场景。某些应用程序会创建后台线程,随着时间的推移将结果传递到共享集合。另一个线程将读取并处理这些结果。您需要在线程之间实现自己的消息传递方案,以便在新结果可用以及这些新结果已被使用时相互通知。 System.Collections.Concurrent 中的类和接口为这些问题和其他涉及以无锁方式跨线程共享数据的常见多线程编程问题提供了一致的实现。

Try具有语义 - try 执行该操作并返回操作结果。 DoThat 语义通常使用异常抛出机制来指示可能效率不高的错误。例如,它们可以返回 false,

  • 如果您尝试添加新元素,您可能已经在 ConcurentDictionary 中拥有它
  • 如果您尝试从集合中获取元素,它可能不存在
  • 如果您尝试更新元素,则可能已经有更新的元素,因此方法确保它仅更新打算更新的元素。

尝试阅读:

Collections in System.Collections.Concurrent namespace are considered to be thread-safe, so it is possible to use them to write multi-threaded programs that share data between threads.

Before .NET 4, you had to provide your own synchronization mechanisms if multiple threads might be accessing a single shared collection. You had to lock the collection each time you modified its elements. You also might need to lock the collection each time you accessed (or enumerated) it. That’s for the simplest of multi-threaded scenarios. Some applications would create background threads that delivered results to a shared collection over time. Another thread would read and process those results. You needed to implement your own message passing scheme between threads to notify each other when new results were available, and when those new results had been consumed. The classes and interfaces in System.Collections.Concurrent provide a consistent implementation for those and other common multi-threaded programming problems involving shared data across threads in lock-free way.

Try<something> has semantics - try to do that action and return operation result. DoThat semantics usually use exception thrown mechanics to indicate error which can be not efficient. As examples there they can return false,

  • if you try add new element, you might already have it in ConcurentDictionary;
  • if you try to get element from collection, it might not exists there;
  • if you try to update element there are can be more recent element already, so method ensures it updates only element which intended to update.

Try to read:

最单纯的乌龟 2024-11-07 15:37:37

失败是什么意思?

采取以下示例:

var queue = new Queue<string>();
string temp = queue.Dequeue();
// do something with temp

上面的代码抛出异常,因为我们尝试从空队列中出队。现在,如果您改用 ConcurrentQueue

var queue = new ConcurrentQueue<string>();
string temp;
if (queue.TryDequeue(out temp))
{
    // do something with temp
}

上面的代码将不会引发异常。队列仍然无法使项目出队,但代码不会以抛出异常的方式失败。其真正用途在多线程环境中变得显而易见。非并发 Queue 的代码通常如下所示:

lock (queueLock)
{
    if (queue.Count > 0)
    {
        string temp = queue.Dequeue();
        // do something with temp   
    } 
}

为了避免竞争条件,我们需要使用锁来确保队列在过去的时间内不会发生任何情况通过检查 Count 来调用 Dequeue。使用 ConcurrentQueue,我们实际上不需要检查 Count,而是可以调用 TryDequeue

如果您检查在 Systems.Collections.Concurrent 命名空间中找到的类型,您会发现其中许多包装了两个通常按顺序调用的操作,并且传统上需要锁定(Count 后跟 ConcurrentQueue 中的 DequeueConcurrentDictionary 中的 GetOrAdd 替换序列调用 ContainsKey、添加项目并获取它,等等)。

What do you mean with fail?

Take the following example:

var queue = new Queue<string>();
string temp = queue.Dequeue();
// do something with temp

The above code with throw an exception, since we try to dequeue from an empty queue. Now, if you use a ConcurrentQueue<T> instead:

var queue = new ConcurrentQueue<string>();
string temp;
if (queue.TryDequeue(out temp))
{
    // do something with temp
}

The above code will not throw an exception. The queue will still fail to dequeue an item, but the code will not fail in the way of throwing an exception. The real use for this becomes apparent in a multithreaded environment. Code for the non-concurrent Queue<T> would typically look something like this:

lock (queueLock)
{
    if (queue.Count > 0)
    {
        string temp = queue.Dequeue();
        // do something with temp   
    } 
}

In order to avoid race conditions, we need to use a lock to ensure that nothing happens with the queue in the time that passes from checking Count do calling Dequeue. With ConcurrentQueue<T>, we don't really need to check Count, but can instead call TryDequeue.

If you examine the types found in the Systems.Collections.Concurrent namespace you will find that many of them wrap two operations that are typically called sequentially, and that would traditionally require locking (Count followed by Dequeue in ConcurrentQueue<T>, GetOrAdd in ConcurrentDictionary<TKey, TValue> replaces sequences of calling ContainsKey, adding an item and getting it, and so on).

冷默言语 2024-11-07 15:37:37

例如,如果没有任何内容需要“出列”...这种“Try-Pattern”在 FCL 和 BCL 元素中普遍使用。这与锁定无关,并发集合(或至少应该)大部分是在没有锁的情况下实现的......

If there is nothing to be "dequeued", for example... This "Try-Pattern" is used commonly all across FCL and BCL elements. That has nothing to do with locking, concurrent collections are (or at least should be) mostly implemented without locks...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文