如何获取ConcurrentDictionary的瞬时快照?

7

MSDN表示从ConcurrentDictionary返回的枚举器并不代表ConcurrentDictionary的瞬时快照。

虽然在多线程环境中很少需要,但如果有需要,如何获取ConcurrentDictionary的瞬时快照是最佳方式?


2
如何为一个可以在多个线程并行更改的集合定义“瞬时快照”?迭代遍历集合以收集其组成部分需要时间,在此期间它可能已被修改。虽然这样做不是不可能,但相当复杂,并且 ConcurrentDictionary 也无法做到。 - xxbbcc
1
@xxbbcc 请查看 ConcurrentBag<T>, ConcurrentQueue<T>ConcurrentStack<T>GetEnumerator() 代码,它们都提供了枚举器的瞬时快照。 - Scott Chamberlain
1
@ScottChamberlain,你的BlockignCollection提案仍然无法在某个时间点给你一个快照。在取出一个项目后,字典的其他用户可能会删除一个项目(或添加一个新项目),从而破坏你的迭代器。 - Servy
1
@IvanStoev对于ConcurrentDictionary,它不会出现这种情况,但对于其他类型则会。这是Scott的观点。 - Servy
1
@Ramy 随意向类的开发人员询问他们为什么以这种方式设计它。 - Servy
显示剩余9条评论
1个回答

13

只需调用 ToArray() 方法即可。

这是一份源代码

    /// <summary>
    /// Copies the key and value pairs stored in the <see cref="ConcurrentDictionary{TKey,TValue}"/> to a
    /// new array.
    /// </summary>
    /// <returns>A new array containing a snapshot of key and value pairs copied from the <see
    /// cref="ConcurrentDictionary{TKey,TValue}"/>.</returns>
    [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "ConcurrencyCop just doesn't know about these locks")]
    public KeyValuePair<TKey, TValue>[] ToArray()
    {
        int locksAcquired = 0;
        try
        {
            AcquireAllLocks(ref locksAcquired);
            int count = 0;
            checked
            {
                for (int i = 0; i < m_tables.m_locks.Length; i++)
                {
                    count += m_tables.m_countPerLock[i];
                }
            }

            KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[count];

            CopyToPairs(array, 0);
            return array;
        }
        finally
        {
            ReleaseLocks(0, locksAcquired);
        }
    }

3
你甚至可以将其链接到 foo.ToArray().ToDictionary(x=>x.Key, x=>x.Value); 以将其转换为字典形式。(不要只使用 foo.DoDictionary(),否则它会使用 .GetEnumerator() 函数而不是 .ToArray() 函数作为数据源)。 - Scott Chamberlain
根据您的需求,.Keys.Values也会为您提供它们各自值的快照。 - xr280xr
这个解决方案存在一个问题,即在这个可能相对较长的循环中,即使是其他读取操作也会导致ConcurrentDictionary被阻塞(例如,ConcurrentDictionary.Count属性源代码会等待内部锁解锁后才能继续执行),尽管它不应该,因为您对其进行的是非变异操作。 - Alexander Abakumov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接