69

I have a class with the following method:

public List<Bike> bikesCopy 
{
     get 
     { 
       List<Bike> bs;
       lock (_bikes) bs = new List<Bike>(_bikes);
       return bs;
     }
}

Which makes a copy of another list, private List<Bike> _bikes;

The strange thing now is, that I get the following error:

Destination array was not long enough. Check destIndex and length, and the array's lower bounds.

What is the problem here?

Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
Geert
  • 795
  • 1
  • 5
  • 7
  • Where exactly do you get the exception? Can you update the question with the exception stack trace and point out the exact code line where it is thrown? – Fredrik Mörk Apr 28 '12 at 08:57
  • Yes, it is the following message (sorry for the mess): `at System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length, Boolean reliable) at System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length) at System.Collections.Generic.List'1.CopyTo(T[] array, Int32 arrayIndex) at System.Collections.Generic.List'1..ctor(IEnumerable'1 collection) at MyGame.Player.get_bikesCopy()` – Geert Apr 28 '12 at 12:49
  • 2
    Did you every get to the root cause of this problem? I am running into something similar. – Klaus Nji Oct 09 '12 at 21:03
  • 6
    If you didn't resolve this, I believe the problem could be that you have not put a lock around the _bikes.Add(_) method (or maybe another List method). This is because the Add method is not thread safe in a List. Anyway, hope that helps if anyone gets the same problem – Theo Oct 22 '12 at 11:18

4 Answers4

126

I would say the error lies in the object _bikes not being thread safe. As commented, somewhere there is a modify of the _bikes object that is not being lock'ed.

It is a split second error where the variable bs is set up to a size X when the size of _bikes is measured. In the next split second as it is about to fill the list, the _bikes object has increased in size giving the error.

So go over your code. Find all references of your _bikes object and make sure they are thread safe handled (with lock).

Wolf5
  • 16,600
  • 12
  • 59
  • 58
7

This error occurs because multiple threads are adding items in a single list. Lists are by default not a thread-safe solution. In a multi-threaded code, it is only recommended to read from a list, and not write to it.

As described here:

If you are only reading from a shared collection, then you can use the classes in the System.Collections.Generic namespace.

Better use a thread-safe solution like System.Collections.Concurrent namespace which provides implementations like ConcurrentBag, ConcurrentDictionary, ConcurrentQueue, ConcurrentStack etc.

For example:

public ConcurrentBag<Bike> bikesCopy 
{
     get => new ConcurrentBag<Bike>()
}
Kush Grover
  • 363
  • 1
  • 6
  • 16
6

Well you could try:

using System.Linq; //ToList() is an extension function defined here
...
lock(_bikes)
    return _bikes.ToList();

The details of the exception are discussed here: Why doesn't a foreach loop work in certain cases?

Community
  • 1
  • 1
edvaldig
  • 2,301
  • 16
  • 17
  • 1
    I am not capable using LINQ, as it is not whitelisted on my framework.... Anyway, it doesn't answer my problem either :) – Geert Apr 28 '12 at 12:42
  • 24
    You can't use LINQ?!?!? I'd quit that job as soon as I found that out! – Serj Sagan Jan 26 '16 at 18:16
  • 1
    Not sure why, but `.ToList()` works great versus `new List(YourList)`, which always throws an exception. –  Jul 13 '16 at 17:29
  • 1
    @JamesM It's the line `_items = new T[capacity];` in the source code for List (https://referencesource.microsoft.com/mscorlib/system/collections/generic/list.cs.html) which makes all the difference. – Xan-Kun Clark-Davis Feb 18 '18 at 22:58
0

Not really an answer, more a research comment.

I ran into the same problem and did a quick test. I tried with the code below and could not get this code to throw the ArgumentException: Destination array was not long enough. But when I remove the .ToList() from the line

return allLines.ToList().ToArray();

it immediately crashes.

This is the demo code and even the IDE tells me, I should remove the ToList() call as it seems redundant.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main() {

            List<string> thelist = new List<string>();

            Thread producer = new Thread(() => {
                while (true) {
                    thelist.Add("a" + DateTime.Now);
                }
            });

            Thread transformer = new Thread(() => {
                while (true) {
                    string[] thearray = thelist.ToList().ToArray();
                    Console.WriteLine(thearray.Length);
                }
            });
            producer.Start();
            transformer.Start();
            Console.ReadKey(true);
        }
    }
}

I really wonder, why it would not crash, as the List is also backed by an array.

Xan-Kun Clark-Davis
  • 2,664
  • 2
  • 27
  • 38