Search code examples
c#multithreadinglisttimerhighspeed

Multiple Timers treating List at high speed causes issue


I'm writing a C# library that needs to treat a List at high speed via multiple Timers. I ran into very erratic error, where I try to remove an element that I know for sure is contained into the List but the program returns the following error :

System.IndexOutOfRangeException : 'index was outside the bounds of the array.'

I've made a simple example to reproduce this behaviour. Because of that issue's randomness, I've pushed hard on List operations so it throws the error right away. So this example is necessary "weird". I've made a public repo on here : Issue Example Repo

Basically, here's what I'm dealing with:

        list = new List<DummyElement>();
        for (int i = 0; i < 1000; i++)
        {
            Timer addTimer = new Timer(0.01f);
            addTimer.Start();
            addTimer.Elapsed += AddItem;
            Timer removeTimer = new Timer(0.01f);
            removeTimer.Start();
            removeTimer.Elapsed += RemoveItem;
        }
        void AddItem(object source, ElapsedEventArgs e)
        {
            list.Add(new DummyElement());
        }

        void RemoveItem(object source, ElapsedEventArgs e)
        {
            int listCount = list.Count;
            if (listCount > 0)                   // This condition is successfully passed, so there is at least one element on the list
            {
                list.RemoveAt(0);            // This line throw an IndexOutOfRangeException error
            }
        }

I believe it is a thread related issue, as if the list count was changing AFTER the condition was successfully passed.

I know nothing about thread, how can I deal with this issue?


Solution

  • In the For loop that goes upto 1000 - you are creating about 1000 Timers that add an item into list and 1000 timers that remove first item.

    Since you haven't used any synchronization here's what happens:- Say there is 1 item in List and 2 RemoveItems are executing. both see listCount > 0 as True then one of them goes ahead and removes the Item at 0th Index while the other gets an Exception cause there is no item to remove now.

    Now I cant suggest a solution to this by just looking at the code. I also need to understand the intent.

    This is a Text Book Producer Consumer problem so the Text book advice here is using Lock construct:

    Assume you have a class member like:

    private object _lockMe = new object();
    
    void RemoveItem(object source, ElapsedEventArgs e)
    {
        lock(_lockMe)
        {
            int listCount = list.Count;
            if (listCount > 0)                   // This condition is successfully passed, so there is at least one element on the list
            {
                list.RemoveAt(0);            // This line throw an IndexOutOfRangeException error
            }
         }
    }