public void EditWhileIteratingThreaded() { var d = new ConcurrentDictionary <string, string>(); Assert.IsTrue(d.TryAdd("0", "1")); Assert.IsTrue(d.TryAdd("a", "b")); int[] expectedCount = { 2 }; Assert.AreEqual(expectedCount[0], d.Count); string a = null; var foundCount = 0; var didAdd = 0; var didRemove = 0; var found = new CircularBucket <string>(16); ThreadStart remover = () => { var removed = d.TryRemove("a", out a); if (Thread.VolatileRead(ref didRemove) == 0 && removed) { expectedCount[0]--; } if (removed) { Interlocked.CompareExchange(ref didRemove, 1, 0); } }; ThreadStart adder = () => { var added = d.TryAdd("c", "d"); if (Thread.VolatileRead(ref didAdd) == 0 && added) { expectedCount[0]++; } if (added) { Interlocked.CompareExchange(ref didAdd, 1, 0); } }; // MSDN says: "it does not represent a moment-in-time snapshot of the dictionary." // And also "The contents exposed through the enumerator may contain modifications made to the dictionary after GetEnumerator was called." foreach (var item in d) { found.Add(item.Key); foundCount++; var old = expectedCount[0]; Assert.AreEqual(expectedCount[0], d.Count); { var t = new Thread(remover); t.Start(); t.Join(); } if (foundCount == 1) { Assert.AreNotEqual(old, expectedCount[0]); } else { Assert.AreEqual(old, expectedCount[0]); } Assert.AreEqual(expectedCount[0], d.Count); old = expectedCount[0]; { var t = new Thread(adder); t.Start(); t.Join(); } if (foundCount == 1) { Assert.AreNotEqual(old, expectedCount[0]); } else { Assert.AreEqual(old, expectedCount[0]); } Assert.AreEqual(expectedCount[0], d.Count); } Assert.IsNull(a); var array = found.ToArray(); if (!array.IsSupersetOf(new[] { "0", "c" })) { foreach (var item in array) { Console.WriteLine(item); } Assert.Fail(); } Assert.AreEqual(2, expectedCount[0]); Assert.AreEqual(1, didAdd); Assert.AreEqual(1, didRemove); Assert.IsTrue(foundCount - expectedCount[0] < 2, "foundCount: {0}, expectedCount:{1}", foundCount, expectedCount[0]); Assert.AreEqual(expectedCount[0], d.Count); }
public void EditWhileIterating() { var d = new ConcurrentDictionary <string, string>(); Assert.IsTrue(d.TryAdd("0", "1")); Assert.IsTrue(d.TryAdd("a", "b")); var expectedCount = 2; Assert.AreEqual(expectedCount, d.Count); string a = null; var foundCount = 0; var didAdd = false; var didRemove = false; var found = new CircularBucket <string>(16); // MSDN says: "it does not represent a moment-in-time snapshot of the dictionary." // And also "The contents exposed through the enumerator may contain modifications made to the dictionary after GetEnumerator was called." // Note: There is no guarantee the items are in insert order foreach (var item in d) { found.Add(item.Key); foundCount++; Assert.AreEqual(expectedCount, d.Count); var removed = d.TryRemove("a", out a); if (didRemove) { Assert.IsFalse(removed); } else { Assert.IsTrue(removed); expectedCount--; } didRemove = didRemove | removed; Assert.IsTrue(didRemove); Assert.AreEqual(expectedCount, d.Count); var added = d.TryAdd("c", "d"); if (didAdd) { Assert.IsFalse(added); } else { Assert.IsTrue(added); expectedCount++; } didAdd = didAdd | added; Assert.IsTrue(didAdd); Assert.AreEqual(expectedCount, d.Count); } Assert.IsNull(a); var array = found.ToArray(); if (!array.IsSupersetOf(new[] { "0", "c" })) { foreach (var item in array) { Console.WriteLine(item); } Assert.Fail(); } Assert.AreEqual(2, expectedCount); Assert.AreEqual(true, didAdd); Assert.AreEqual(true, didRemove); Assert.IsTrue(foundCount - expectedCount < 2, "foundCount: {0}, expectedCount:{1}", foundCount, expectedCount); Assert.AreEqual(expectedCount, d.Count); }