C
C#3y ago
DeaDo

Why is my code not thread save?

await Task.WhenAll(tasks).ContinueWith(task => Selection());
await Task.WhenAll(tasks).ContinueWith(task => Selection());
is in a loop tasks is a list of tasks which add elements to a list; Selection is a method which edits the list (cuts off the worse results that were delivered when "tasks" were executed) I thought that all tasks are finished when starting Selection() and i start it like this afterwards. But i always get the following error after a few cycles:
System.ArgumentException: 'Destination array was not long enough. Check the destination index, length, and the array's lower bounds. Arg_ParamName_Name'
System.ArgumentException: 'Destination array was not long enough. Check the destination index, length, and the array's lower bounds. Arg_ParamName_Name'
7 Replies
canton7
canton73y ago
I don't think you've shared enough code for us to be able to help: we can't see what tasks or Selection are doing My best guess is that you're starting a bunch of tasks on different threads which all add to the same list, but without more info that's just a guess
DeaDo
DeaDoOP3y ago
The program should help solving the "traveling-salesman-problem" It creates a bunch of tracks from city to city, checks the travel distance of each track and adds the track to a list. After creating 100 tracks, it takes the 10 shortest and adds mutated variants of those to the list until there are 100 again. Class Track serves as data-object for storing the track with its distance and the track it mutated from.
private List<Track> tracks = new List<Track>();

public async void CreateNextGen()
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < genSize - nextGenSize; i++)
{
if (rnd.Next(100) == 50)
{
tasks.Add(Task.Run(() => AddSwapped()));
}
else
{
tasks.Add(Task.Run(() => AddMutation()));
}
}
await Task.WhenAll(tasks);
//sleep here
Selection();
}

private void AddMutation()
{
var track = tracks[rnd.Next(nextGenSize)];
var mutation = CreateMutation(track.CityIndexes);
var distance = GetTrackDistance(mutation);
tracks.Add(new Track(mutation, distance, track));
}
private void AddSwapped()
{
var track = tracks[rnd.Next(nextGenSize)];
var swapped = SectionSwap(track.CityIndexes);
var distance = GetTrackDistance(swapped);
tracks.Add(new Track(swapped, distance, track));
}

private void Selection()
{
tracks = tracks.OrderBy(t => t.Distance).Take(nextGenSize).ToList();
}
private List<Track> tracks = new List<Track>();

public async void CreateNextGen()
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < genSize - nextGenSize; i++)
{
if (rnd.Next(100) == 50)
{
tasks.Add(Task.Run(() => AddSwapped()));
}
else
{
tasks.Add(Task.Run(() => AddMutation()));
}
}
await Task.WhenAll(tasks);
//sleep here
Selection();
}

private void AddMutation()
{
var track = tracks[rnd.Next(nextGenSize)];
var mutation = CreateMutation(track.CityIndexes);
var distance = GetTrackDistance(mutation);
tracks.Add(new Track(mutation, distance, track));
}
private void AddSwapped()
{
var track = tracks[rnd.Next(nextGenSize)];
var swapped = SectionSwap(track.CityIndexes);
var distance = GetTrackDistance(swapped);
tracks.Add(new Track(swapped, distance, track));
}

private void Selection()
{
tracks = tracks.OrderBy(t => t.Distance).Take(nextGenSize).ToList();
}
since i have >10000 cities there are factorial of 10000 different ways to create a track/route, so i cant just check every possible route. So i went for a ML-like approach
canton7
canton73y ago
Yeah, both AddSwapped and AddMutation mutate tracks And tracks isn't thread-safe
DeaDo
DeaDoOP3y ago
i guess there is no easy solution for this? it was not intended for multithreading in the first place and i dont have experience with that as well does it change something if i make a copy of the track before AddMutation/AddSwapped change tthat one?
canton7
canton73y ago
Don't access tracks from those methods, but pass them a track as a parameter, and have them return the track Then the caller can sort all of that out (since CreateNextGen runs on a single thread)
DeaDo
DeaDoOP3y ago
public async void CreateNextGen()
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < genSize - nextGenSize; i++)
{
if (rnd.Next(100) == 50)
{
tasks.Add(Task.Run(() => AddSwapped(tracks[rnd.Next(nextGenSize)])));
}
else
{
tasks.Add(Task.Run(() => AddMutation(tracks[rnd.Next(nextGenSize)])));
}
}
await Task.WhenAll(tasks);
//sleep here
Selection();
}

private Track AddMutation(Track track)
{
var mutation = CreateMutation(track.CityIndexes);
var distance = GetTrackDistance(mutation);
return new Track(mutation, distance, track);
}
private Track AddSwapped(Track track)
{
var swapped = SectionSwap(track.CityIndexes);
var distance = GetTrackDistance(swapped);
return new Track(swapped, distance, track);
}
public async void CreateNextGen()
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < genSize - nextGenSize; i++)
{
if (rnd.Next(100) == 50)
{
tasks.Add(Task.Run(() => AddSwapped(tracks[rnd.Next(nextGenSize)])));
}
else
{
tasks.Add(Task.Run(() => AddMutation(tracks[rnd.Next(nextGenSize)])));
}
}
await Task.WhenAll(tasks);
//sleep here
Selection();
}

private Track AddMutation(Track track)
{
var mutation = CreateMutation(track.CityIndexes);
var distance = GetTrackDistance(mutation);
return new Track(mutation, distance, track);
}
private Track AddSwapped(Track track)
{
var swapped = SectionSwap(track.CityIndexes);
var distance = GetTrackDistance(swapped);
return new Track(swapped, distance, track);
}
how do i collect the results in CreateNextGen? Ok, i can google that myself. Thanks a lot! 👍
Angius
Angius3y ago
Also, you really shouldn't be using .ContinueWith() It's really just an older and worse version of what await provides
Want results from more Discord servers?
Add your server