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?
4 Answers 4
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).
4 Comments
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>()
}
Comments
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?
4 Comments
_items = new T[capacity];
in the source code for List<T> (referencesource.microsoft.com/mscorlib/system/collections/…) which makes all the difference.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.
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()