Как получить моментальный снимок ConcurrentDictionary в С#?

MSDN указывает, что перечислитель, возвращенный из словаря, не представляет момент- моментальный снимок словаря. Хотя это редко требуется в многопоточной среде, но если нужно, как лучше всего получить моментальный снимок ConcurrentDictionary?


person Ramy    schedule 11.05.2017    source источник
comment
Как определить моментальный снимок для коллекции, которую можно изменять в нескольких потоках параллельно? Итерация по коллекции для сбора элементов, из которых она состоит, требует времени, и за это время она уже может быть изменена. Это не невозможно сделать, но это довольно сложно, и ConcurrentDictionary этого не делает.   -  person xxbbcc    schedule 11.05.2017
comment
@xxbbcc см. код GetEnumerator() для ConcurrentBag<T>, ConcurrentQueue<T> или ConcurrentStack<T>, все они предоставляют моментальный снимок счетчика во времени.   -  person Scott Chamberlain    schedule 11.05.2017
comment
ToArray?   -  person Ivan Stoev    schedule 11.05.2017
comment
Из документации enumeration представляет моментальный снимок содержимого очереди. Он не отражает никаких обновлений коллекции после вызова GetEnumerator. Перечислитель можно безопасно использовать одновременно с чтением из очереди и записью в нее.   -  person Scott Chamberlain    schedule 11.05.2017
comment
@ScottChamberlain Хорошо, я этого не знал. Спасибо.   -  person xxbbcc    schedule 11.05.2017
comment
@xxbbcc, чтобы получить версию без моментального снимка, вы должны обернуть коллекцию в BlockingCollection, а затем вызвать для нее .GetConsumingEnumerable(). Он предоставляет перечисляемое с оперативными данными и удаляет элементы из коллекции, поскольку элементы возвращаются в перечисляемое.   -  person Scott Chamberlain    schedule 11.05.2017
comment
@ScottChamberlain Эти счетчики должны предотвращать мутации в коллекции во время их перечисления. Поскольку потребитель, пытающийся получить моментальный снимок параллельного словаря, не может предотвратить его изменение кем-либо еще во время его итерации, мы не можем получить такой снимок.   -  person Servy    schedule 11.05.2017
comment
@ScottChamberlain Ваше предложение BlockignCollection все еще не даст вам моментальный снимок вовремя. После извлечения одного элемента другой пользователь словаря может удалить элемент (или добавить новый), что испортит ваш итератор.   -  person Servy    schedule 11.05.2017
comment
@Servy, я не слежу за твоим комментарием. 3 класса, которые я упоминаю, которые выполняют моментальный снимок, позволяют добавлять и удалять в коллекцию, пока вы перечисляете перечисляемое, вы просто не видите эти добавления или удаления в IEnumerable. также я никогда не говорил, что BlockingCollection работает с ConcurrentDictionary или что он дает вам версию снимка   -  person Scott Chamberlain    schedule 11.05.2017
comment
@IvanStoev Для ConcurrentDictionary это не так, для других типов это так. Это была точка зрения Скотта.   -  person Servy    schedule 11.05.2017
comment
@Servy, почему не для Concurrent Dictionary, а для других типов?   -  person Ramy    schedule 11.05.2017
comment
@Ramy Не стесняйтесь спрашивать разработчиков класса, почему они разработали его именно так.   -  person Servy    schedule 11.05.2017


Ответы (1)


Просто вызовите метод 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);
        }
    }
person apocalypse    schedule 11.05.2017
comment
Вы даже можете связать его с foo.ToArray().ToDictionary(x=>x.Key, x=>x.Value);, чтобы вернуть его в словарной форме. (Не делайте только foo.DoDictionary(), иначе в качестве источника данных будет использоваться функция .GetEnumerator() вместо функции .ToArray()) - person Scott Chamberlain; 11.05.2017