none
c# ConcurrentDictionary and simulare read write at the same time RRS feed

  • Question

  • I want to create a Unit Test which at the same time

    1. write add to ConcurrentDictionary

    2. read and remove keys from the ConcurrentDictionary

    both of them at the same time

    what the best way to test it?

    Thursday, May 30, 2019 10:20 AM

All replies

  • Hello,

    Can you post your current code effort so those who might assist have a base to go by.


    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    StackOverFlow
    profile for Karen Payne on Stack Exchange

    Thursday, May 30, 2019 10:47 AM
  • You really cannot guarantee that two threads are writing/reading from a concurrent dictionary at the same time. There simply isn't any reliable way to confirm this. You can certainly set up tests that most likely will do this (or at least as close as you can get) but that is about it. However I question while you're doing this. You don't need to unit test framework types. You can assume they work as advertised. The only thing you should be unit testing is your code.

    Nevertheless to "simulate" what would happen if multiple threads were reading/writing to a concurrent dictionary at the same time you can use a couple of Task items. Have each task write, read and then remove a series of keys from the dictionary. To help simulate what might happen in a real world scenario use Task.Yield between each call. If you put this into a loop and run it for a reasonable # of iterations you can assume that at some point it'll be running both sets of tasks at the same time. Again, though, there is no way to actually confirm that they are occurring at the same time.

    //Not tested
    var target = new ConcurrentDictionary<int>();
    var taskA = DoWork(1, 100);
    var taskB = DoWork(100000, 100);
    
    //Wait for them to finish
    await taskA;
    await taskB;
    
    async Task DoWork ( ConcurrentDictionary<int> target, int start, int count )
    {
       for (var index = start; index <= count; ++index)
       {
          await Task.Yield();
          var actual = target.TryAdd(index, index);
          Assert.IsTrue(actual);
    
          await Task.Yield();
          actual = target.TryGetValue(index, out var getValue);
          Assert.IsTrue(actual);
    
          await Task.Yield();
          actual = target.TryRemove(index, out var removedValue);
          Assert.IsTrue(actual);
       };
    }
    In this particular test you have 2 different tasks running at the same time and each is trying to insert a series of values into the dictionary. In each case the value is added, retrieved and then removed. You can get as simple or complex as you want. You can also change things around to perhaps insert everything first, then retrieve them and lastly remove. It doesn't really matter too much. Since they are separate tasks and you're using Yield between each operation there is a good chance (on a multi-processor machine) that they will overlap at least a little.


    Michael Taylor http://www.michaeltaylorp3.net

    Thursday, May 30, 2019 10:01 PM
  • Hi 

    Thank you for posting here.

    Since this thread is related to unit test, I will move it to unit testing forum to get support.

    https://social.msdn.microsoft.com/Forums/vstudio/en-US/home?forum=vsunittest

    The Visual C# forum discusses and asks questions about the C# programming language, IDE, libraries, samples, and tools.

    Best Regards,

    Jack


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, May 31, 2019 6:01 AM
  • ConcurrentDictionary methods are thread-safe, but that does not apply to the methods or properties of items in the dictionary. It can experience problems with adding values during high concurrency. ConcurrentDictionary.GetOrAdd may invoke the valueFactory multiple times per key.

    If you are using this type to store large or expensive objects (such as unmanaged resources or database connections), then any accidental instantiation of multiple of these could be a real problem for your application.

    One solution is to create Lazy wrappers for any expensive objects you need. By using Lazy, it will not matter how many times the valueFactory is called, because only one instance of the resource itself will ever be accessed and instantiated.

        [TestClass]
        public class ConcurrentDictionaryTests
        {
            [TestMethod]
            public void SuccessTest()
            {
                var concurrentDictionary = new ConcurrentDictionary<int, Lazy<int>>();
                var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 100 };
                var addStack = new ConcurrentStack<int>();
                var lazyStack = new ConcurrentStack<int>();
    
                Parallel.For(1, 1000, parallelOptions, i =>
                {
                    var key = i % 10;
                    concurrentDictionary.GetOrAdd(key, k =>
                    {
                        addStack.Push(k);
                        return new Lazy<int>(() =>
                        {
                            lazyStack.Push(k);
                            return i;
                        });
                    });
                });
    
                // Access the dictionary values to create lazy values.
                foreach (var pair in concurrentDictionary)
                {
                    Assert.IsNotNull(pair.Value.Value);
                }
    
                // Assert that there are 10 items in our dictionary.
                Assert.AreEqual(10, concurrentDictionary.Count);
    
                // Assert that the add was invoked more than 10 times.
                Assert.IsTrue(10 < addStack.Count);
    
                // Assert that only 10 values were actually created.
                Assert.AreEqual(10, lazyStack.Count);
            }
    
            [TestMethod]
            public void FailureTest()
            {
                var concurrentDictionary = new ConcurrentDictionary<int, int>();
                var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 100 };
                var addStack = new ConcurrentStack<int>();
    
                Parallel.For(1, 1000, parallelOptions, i =>
                {
                    var key = i % 10;
                    concurrentDictionary.GetOrAdd(key, k =>
                    {
                        addStack.Push(k);
                        return i;
                    });
                });
    
                // Assert that there are 10 items in our dictionary.
                Assert.AreEqual(10, concurrentDictionary.Count);
    
                // Assert that the add was invoked more than 10 times.
                Assert.IsTrue(10 < addStack.Count);
            }
        }


    william xifaras


    Friday, May 31, 2019 6:48 PM
  • thanks I will check and revert
    Tuesday, June 4, 2019 11:48 AM
  • Hi want 2 Learn,

    Sorry to trouble you and just want to confirm that if your question is solved or not, if not, please feel free to let us know, we will try our best to help you out.

    BTW, if you think the reply is helpful and could please mark it as answer, that benefits for other community members who meet the similar issue, thanks in advance.

    If you have any other VS IDE issues, please let us know.

    Have a nice day!

    Best Regards,

    Dylan


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com

    Thursday, June 6, 2019 3:20 AM