简体   繁体   中英

System.ArgumentException when locking

In my real application I need to iterate collection, but it can be changed from other thread. So I need to copy collection to iterate on it. I reproduced this problem to small example, but apparently my lack of understanding of locks and threads results in System.ArgumentException . Tried different things with lock, but result is the same.

class Program
{
    static List<int> list;
    static void Main(string[] args)
    {
        list = new List<int>();
        for (int i = 0; i < 1000000; i++)
        {
            list.Add(i);
            if (i == 1000)
            {
                Thread t = new Thread(new ThreadStart(WorkThreadFunction));
                t.Start();
            }
        }
    }

    static void WorkThreadFunction()
    {
        lock (list)
        {
            List<int> tmp = list.ToList();  //Exception here!
            Console.WriteLine(list.Count);
        }
    }
}

Option 1:

Here's a modified version of your code:

class Program
{
    static List<int> list;
    static void Main(string[] args)
    {
        list = new List<int>();
        for (int i = 0; i < 1000000; i++)
        {
            lock (list) //Lock before modification
            {
                list.Add(i);
            }
            if (i == 1000)
            {
                Thread t = new Thread(new ThreadStart(WorkThreadFunction));
                t.Start();
            }

        }

        Console.ReadLine();
    }

    static void WorkThreadFunction()
    {
        lock (list)
        {
            List<int> tmp = list.ToList();  //Exception here!
            Console.WriteLine(list.Count);
        }
    }
}

What happens here is that your list is being modified while being converted to another list collection (where argument exception is happening). So to avoid that you will need to lock the list as shown above.

Option 2: (No lock )

Using Concurrent collections to remove the lock :

using System.Collections.Concurrent;

//Change this line
static List<int> list;
//To this line
static ConcurrentBag<int> list;

And remove all lock statements.

I see some issues in your algorithm, and may be you should refactor it. In case of using either locks or ConcurrentBag class you should realize that the copying entire collection into new one simply for enumeration is very huge and very time-consuming operation, and during it you can't operate with collection efficiently.

lock (list)
{
    // VERY LONG OPERATION HERE
    List<int> tmp = list.ToList();  //Exception here!
    Console.WriteLine(list.Count);
}

You really shouldn't lock collection for such amount of time - at the end of the for loop you have a lot of Threads which are blocking each other. You have to use the TPL classes for this approach and shouldn't use Threads directly.

The other case you can choose is to implement some of optimistic lock-free algorithm with double check for the collection version, or even lock-free and wait-free algorithm with storing the snapshot of the collection and checking for it inside your methods for the collection access. Additional information can be found here .

I think that the information you gave isn't enough to suggest you the right way to solve your issue.

Tried Joel's suggestions. ConcurrentBag was very slow. Locking at each of millions iteration seems inefficient. Looks like Event Wait Handles are good in this case (takes 3 time less than with locks on my pc).

 class Program
{
    static List<int> list;
    static ManualResetEventSlim mres = new ManualResetEventSlim(false);

    static void Main(string[] args)
    {
        list = new List<int>();
        for (int i = 0; i < 10000000; i++)
        {
            list.Add(i);

            if (i == 1000)
            {
                Thread t = new Thread(new ThreadStart(WorkThreadFunction));
                t.Start();
                mres.Wait();
            }
        }
    }

    static void WorkThreadFunction()
    {
        List<int> tmp = list.ToList();
        Console.WriteLine(list.Count);
        mres.Set();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM