In C #, as iterating IEnumerable in a multi-threaded environment

I am in this situation that there is a large dictionary that is accidentally updated by one thread at a fairly high frequency, and there is another thread that tries to take a picture of the dictionary to save it as a story. I am currently using something like this:

Dictionary<string, object> dict = new Dictionary<string, object>();
var items = dict.Values.ToList();

This works fine for most of the time, except that it sometimes throws:

System.InvalidOperationException: The collection has been modified; enumeration operation cannot be performed.

I understand why this happens, but I do not know what I can do to avoid a modified collection error.

What is the best approach to iterating such a collection?

I also tried ConcurrentDictionary, but no luck. What for? Is ConcurrentDictionary a thread safe at the element level only?

+6
source share
3 answers

According to the docs, you can use the GetEnumerator()ConcurrentDictionary method to get the iterator with the stream.

The enumerator returned from the dictionary is safe for simultaneous use with reading and writing to the dictionary, however it does not represent a snapshot of the dictionary in time. Content displayed through the counter may contain changes made to the dictionary after calling GetEnumerator.

, , , , , . , :

var items = concurrentDict.Items.ToList();

var items = concurrentDict.GetEnumerator();

:

foreach(var item in concurrentDict)
{
    valueList.Add(item.Value);
}
0

ImmutableDictionary , .

// initialize.
ImmutableDictionary<string, int> dict = ImmutableDictionary.Create<string,int>();

// create a new dictionary with "foo" key added.
ImmutableDictionary<string, int> newdict = dict.Add("foo", 0);

// replace dict, thread-safe, with a new dictionary with "bar" added.
// note this is using dict, not newdict, so there is no "foo" in it.
ImmutableInterlocked.TryAdd(ref dict, "bar", 1);

// take a snapshot, thread-safe.
ImmutableDictionary<string,int> snapshot = dict;

, - , . - " " , , .

, , . , , ConcurrentDictionary, , . , a ConcurrentDictionary , .

+1

lock, , .

public class SnapshotDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
    private readonly Dictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>();
    private readonly object _lock = new object();

    public void Add(TKey key, TValue value)
    {
        lock (_lock)
        {
            _dictionary.Add(key, value);
        }
    }

    // TODO: Other necessary IDictionary methods

    public Dictionary<TKey, TValue> GetSnaphot()
    {
        lock (_lock)
        {
            return new Dictionary<TKey, TValue>(_dictionary);
        }
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return GetSnaphot().GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

GetSnapshot .
GetEnumerator , , .

, , :

var items = snapshotDictionary.GetSnapshot().Values.ToList();

// or 

foreach (var item in snapshotDictionary)
{
    // ...
}

.

0
source

All Articles