Threading modifying unintended variable [duplicate] - c#

This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed 9 years ago.
I'm fairly new to C# threading, so apologies in advance for this newbie error. The aim of the following code is to evaluate the fitness of a population that is stored in an array called members. The procedure members.calculateFitness() is a black box (i.e. I can't modify it to improve performance), so I'm trying to setup threads that call the black box simultaneously, and each thread will deal with 1/THREAD_COUNT population members (so if THREAD_COUNT=4, each thread will deal with 1/4 of the population).
In the first for loop I initialise each thread. In the second for loop, I start the thread.
public void ThreadedPrintPopFitness ()
{
int THREAD_COUNT = 1;
int membersPerThread = members.Length / THREAD_COUNT;
Thread[] fitnessCalculator = new Thread[THREAD_COUNT];
int[] threadResult = new int[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
int start = i * membersPerThread;
int stop = (i+1) * membersPerThread;
fitnessCalculator [i] = new Thread (() => getMaxFitness (members, start, stop, ref threadResult [i]));
}
for (int i = 0; i < THREAD_COUNT; i++) {
fitnessCalculator [i].Start ();
}
for (int i = 0; i < THREAD_COUNT; i++) {
fitnessCalculator [i].Join ();
}
int maxFitness = 0;
for (int i = 0; i < THREAD_COUNT; i++) {
if (maxFitness < threadResult [i])
maxFitness = threadResult [i];
}
Console.WriteLine ("(ThreadedCount) Fittest Population Member's Fitness: " + maxFitness);
}
private static void getMaxFitness (PopulationMember[] members, int start, int stop, ref int result)
{
int maxFitness = 0;
for (int i = start; i < stop && i < members.Length; i++) {
if (members [i].calculateFitness () > maxFitness) {
maxFitness = members [i].lastFitness;
}
}
result = maxFitness;
}
Stepping through the code shows that it gets into the second for loop, and then jumps back to the first for loop and declares an IndexOutOfBoundsException on the integer i. I can see that i = THREAD_COUNT (I've tried with different numbers for THREAD_COUNT).
I'm completely baffled, what am I doing wrong?
Thanks in advance!

Servy has it spot on, I even remember reading about this in John Skeet's book now. I had to make a copy of i within the for loop, this fixed it.
Thanks!

As written in the comments, i is captured, and when increased, the function refers to the new value.
What you should do is copy its value to a local variable:
for (int i = 0; i < THREAD_COUNT; i++) {
int start = i * membersPerThread;
int stop = (i+1) * membersPerThread;
int resultId = i;
fitnessCalculator [i] = new Thread (() => getMaxFitness (members, start, stop, ref threadResult [resultId]));
}

Related

C# Threading... not understanding what why my loop at is 5;

class Thread1_8
{
static int shared_total;
static int thread_count;
static readonly object locker = new object();
static void Main()
{
int[,] arr = new int[5, 5] { { 1,2,3,4,5}, {5,6,7,8,9}, {9,10,11,12,13}, {13,14,15,16,17}, { 17,18,19,20,21} };
for(int i = 0; i < 5; i++)
{
new Thread(() => CalcArray(i, arr)).Start();
}
while(thread_count < 5)
{
}
Console.WriteLine(shared_total);
}
static void CalcArray(int row, int[,] arr)
{
int length = arr.GetLength(0);
int total = 0;
for(int j = 0; j < length; j++)
{
total += arr[row,j];
}
lock (locker)
{
shared_total += total;
thread_count++;
}
}
}
I keep getting a System.IndexOutOfRangeException.
The reason is because for some reason my "i" in the initial for loop is being set to 5 and STILL entering the loop for some reason. I don't understand why. At first I thought it might be because the each thread I create is incrementing the Array but it shouldn't since a Thread has a complete separate execution path, it should jump straight to the CalcArray method.
The reason why this happens is subtle: when you do this
for(int i = 0; i < 5; i++) {
new Thread(() => CalcArray(i, arr)).Start();
}
variable i goes through values 0 all the way to 5, at which point the loop stops, because 5 < 5 evaluates to false.
The thread starts after the loop is over, so the value of row that it sees is 5, not 0 through 4.
This can be fixed by making a local variable row inside the loop.
Another problem in your code is checking thread_count inside Main without locking, in a busy loop. This is not the best process of synchronizing with your threads. Consider using ManualResetEventSlim instead.

Is there some sort of algorithm to continue looping through an unknown amount of child members? [duplicate]

This question already has answers here:
Depth-first flattened collection of an object hierarchy using LINQ
(4 answers)
Closed 8 years ago.
I have the following object set up:
public class DrawingInstance
{
public string DrawingNum;
public string Rev;
public string Title;
public int LevelNum;
public string RefDesc;
public string DateRelease;
public string DrawingType;
public DrawingInstance ParentMember;
public int PageInstance;
public List<DrawingInstance> ChildMembers = new List<DrawingInstance>();
}
After gathering all of the data, I am currently accessing each child member one level at a time, like so:
for (int i = 0; i < drawingInstance.ChildMembers.Count; i++)
{
for (int j = 0; j < drawingInstance.ChildMembers[i].ChildMembers.Count; j++)
{
....
....
}
}
The number of levels in the file being processed can be different each time.
Is there a way through recursion to loop through and traverse an infinite number of levels? I need to process them 1 level at a time. So all of the i's will be processed, then all of the j's for each i are processed, and so on. Currently I have 10 blocks of code for processing a possible of 10 levels, but I feel like there has to be a better way to go about this.
EDIT
Thanks for the quick responses.
Here is a more detailed look straight from my code that gives a little more insight into how I am currently processing the objects
//Level 0 Pages
int _pageNum = PageNum;
int startIdx = 0;
int pageCount = 0;
pageCount = GetVisioPageCount(_treeArray.ChildMembers.Count);
for (int i = 0; i < pageCount; i++)
{
VisioSheetOutline tempSheet = new VisioSheetOutline();
tempSheet = GetSingleSheet(_treeArray, startIdx, _pageNum, (i + 1));
for (int cMember = 0; cMember < tempSheet.ChildPairs.Length; cMember++)
{
ParentDictionary.Add(tempSheet.ChildPairs[cMember].SingleInstance, tempSheet.SheetName);
}
SheetList.Add(tempSheet);
_pageNum++;
startIdx += 15;
}
//Level 1 Pages
for (int i = 0; i < _treeArray.ChildMembers.Count; i++)
{
pageCount = 0;
pageCount = GetVisioPageCount(_treeArray.ChildMembers[i].ChildMembers.Count);
startIdx = 0;
for (int j = 0; j < pageCount; j++)
{
VisioSheetOutline tempSheet = new VisioSheetOutline();
tempSheet = GetSingleSheet(_treeArray.ChildMembers[i], startIdx, _pageNum, (i + 1));
for (int cMember = 0; cMember < tempSheet.ChildPairs.Length; cMember++)
{
ParentDictionary.Add(tempSheet.ChildPairs[cMember].SingleInstance, tempSheet.SheetName);
}
SheetList.Add(tempSheet);
_pageNum++;
startIdx += 15;
}
}
//Level 2 Pages
for (int i = 0; i < _treeArray.ChildMembers.Count; i++)
{
for (int j = 0; j < _treeArray.ChildMembers[i].ChildMembers.Count; j++)
{
pageCount = 0;
pageCount = GetVisioPageCount(_treeArray.ChildMembers[i].ChildMembers[j].ChildMembers.Count);
startIdx = 0;
for (int k = 0; k < pageCount; k++)
{
VisioSheetOutline tempSheet = new VisioSheetOutline();
tempSheet = GetSingleSheet(_treeArray.ChildMembers[i].ChildMembers[j], startIdx, _pageNum, (i + 1));
for (int cMember = 0; cMember < tempSheet.ChildPairs.Length; cMember++)
{
ParentDictionary.Add(tempSheet.ChildPairs[cMember].SingleInstance, tempSheet.SheetName);
}
SheetList.Add(tempSheet);
_pageNum++;
startIdx += 15;
}
}
}
I am currently looking into a few of the suggestions that were made to see which one fits my particular need.
Yes, as you suggested, you can easily deal with this using recursion; you just need a recursive function:
public void ProcessDrawingData(DrawingInstance instance)
{
// Do processing
foreach (DrawingInstance d in ChildMembers)
ProcessDrawingData(d);
}
Call it with the parent instance. This won't do a true breadth-first traversal though, as the first child will execute its first childs children (first all the way down) and slowly unwind.
Microsoft's Ix-Main package contains a number of LINQ extensions, including the Expand method which will flatten a hierarchical layout:
IEnumerable<DrawingInstance> rootList = ...;
IEnumerable<DrawingInstance> flattened = rootList.Expand(x => x.ChildMembers);
You can use the foreach() statement. This will iterate through a group that you need, assuming the object implements IEnumerable.
In your case, try this:
foreach(DrawingInstance di in ChildMembers)
{
// Do something with di.
}
EDIT
If you need to do this repeatedly, you should have some sort of a recursive method that takes a DrawingInstance, like this:
public void RecursiveMethod(DrawingInstance d)
{
foreach(DrawingInstance di in d.ChildMembers)
{
RecursiveMethod(di);
}
}
I don't know your project, so it is up to you to figure out the base case, or if this recursive edit is what you actually want.

Use array of threads

I'm new to threads so it might be an easy one for you, but I've spent some hours trying to figure it out.
Let's say I have a function
public double Gain(List<int> lRelevantObsIndex, ushort uRelevantAttribute)
which needs some time to finish, but is a read only func.
I have an array of ushort[] values, and I want to get the ushort value that achieves the minimum value of the Gain function.
Here is what I've got so far, but it's not working:
lRelevantObsIndex is a read only index.
lRelevantAttributes is the list of ushort values.
//Initialize the threads
double[] aGains = new double[lRelevantAttributes.Count];
Thread[] aThreads = new Thread[lRelevantAttributes.Count];
for (int i = 0; i < lRelevantAttributes.Count; i++)
{
aThreads[i] = new Thread(() => aGains[i] = Gain(lRelevantObsIndex, lRelevantAttributes[i]));
aThreads[i].Start();
}
//Join the threads
for (int i = 0; i < lRelevantAttributes.Count; i++)
aThreads[i].Join();
//The easy part - find the minimum once all threads are done
ushort uResult = 0;
double dMinGain = UInt16.MaxValue;
for (int i = 0; i < lRelevantAttributes.Count; i++)
{
if (aGains[i] < dMinGain)
{
dMinGain = aGains[i];
uResult = lRelevantAttributes[i];
}
}
return uResult;
I know this is a simple multithreading question - but still need your brains since I'm new to this.
This one is somewhat tricky: your for loop uses a modified value here (a so-called access to modified closure)
for (int i = 0; i < lRelevantAttributes.Count; i++)
{
aThreads[i] = new Thread(() => aGains[i] = Gain(lRelevantObsIndex, lRelevantAttributes[i]));
aThreads[i].Start();
}
At the time the thread starts, i will be different in your lambda, accessing a wrong item. Modify your loop as follows:
for (int ii = 0; ii < lRelevantAttributes.Count; ii++)
{
var i = ii; // Now i is a temporary inside the loop, so its value will be captured instead
aThreads[i] = new Thread(() => aGains[i] = Gain(lRelevantObsIndex, lRelevantAttributes[i]));
aThreads[i].Start();
}
This will fix the problem, because lambdas will capture the current value of the temporary variable i on each iteration of the loop.
I'm not sure if this is your problem, but it is a problem:
for (int i = 0; i < lRelevantAttributes.Count; i++)
{
aThreads[i] = new Thread(() => aGains[i] = Gain(lRelevantObsIndex, lRelevantAttributes[i]));
aThreads[i].Start();
}
When a lambda refers to a loop variable, the binding is delayed, so that when your lambda actually runs, it takes the value of i at the time the lambda runs, not the value it had when the lambda was created. To fix this, declare a secondary variable inside the loop, and use that in the lambda:
for (int i = 0; i < lRelevantAttributes.Count; i++)
{
int j = i;
aThreads[i] = new Thread(() => aGains[j] = Gain(lRelevantObsIndex, lRelevantAttributes[j]));
aThreads[i].Start();
}
You can do the same on Task
[Fact]
public void Test()
{
List<Task<int>> tasks = Enumerable.Range(0, 5) //- it's equivalent how many threads
.Select(x => Task.Run(() => DoWork(x)))
.ToList();
int[] result = Task.WhenAll(tasks).Result; //- Join threads
result.ToList().ForEach(Console.WriteLine);
}
private int DoWork(int taskId)
{
return taskId;
}
Result output:
3
0
1
2
4

Strange behaviour when threads using local var [duplicate]

This question already has answers here:
creating new threads in a loop
(2 answers)
Closed 9 years ago.
I wrote a simple code
void go()
{
int i = 5;
ThreadPool.QueueUserWorkItem(delegate
{
for (int j = 1; j <= 1000; j++)
Console.Write(i);
});
for (int k = 1; k <= 1000; k++)
i = k;
Console.ReadLine();
}
What I got is : 100010001000100010001000....
I don't know why, I hope someone can explain it to me, thanks!
The loop that assigns k to i is run before the queued threads are being started.
So, at the time the queued threads are started, i has the value 1000 which is what you see.
Change the code to something like
void go()
{
int i = 5;
int i1 = i; //note this
ThreadPool.QueueUserWorkItem(delegate
{
for (int j = 1; j <= 1000; j++)
Console.Write(i1); //and note this
});
for (int k = 1; k <= 1000; k++)
i = k;
Console.ReadLine();
}
Resharper reports "Access to modified closure".

Simple program not working

this is the first time i work on C# and ASP.NET, i'm reading Beginning ASP.NET 4 in c# 2010 but i never worked with object-oriented programming. (I'm a network admin so i know basic programming).
Still, i can't get to work a simple program
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2 {
class Program {
static void Main(string[] args) {
int i;
string[] nomi = new string[7];
string[] cognomi = new string[7];
for (i = 0; i = 6; i++) {
Console.WriteLine("Inserisci il", i + 1, "° nome");
nomi[i] = (Console.ReadLine);
}
for (i = 0; i = 6; i++) {
Console.WriteLine("Inserisci il", i + 1, "° cognome");
nomi[i] = (Console.ReadLine);
}
for (i = 0; i = 6; i++) {
Console.WriteLine(nomi[i], " ", cognomi[i]);
}
}
}
}
The problem is not in the algorythm but in the declaration part. I can't show the error log because i have VS in Italian.
Where am i wrong?
It's tough to help without error messages (maybe you can translate them since you obviously know both languages).
For sure, your for loop syntax is incorrect. This:
for (i = 0; i = 6; i++)
Should be:
for (i = 0; i < 6; i++) // Or <=, or whatever
Take a look at for loop documentation on MSDN.
In addition Console.ReadLine is a method, not a property:
nomi[i] = Console.ReadLine();
Your for loops are incorrect. They should be
for (int i = 0; i < 7; i++)
{
}
Or, better,
for (int i = 0; i < nomi.Length; i++)
{
}
What this is saying is that i should start at 0, continue looping until the value if i is no longer less than 7 (or the value of nomi.Length) and i should increase by 1 each loop (i++).
Also, unrelated to the loops, it should be Console.ReadLine();. The () shows that it's a method that takes no parameters, whereas nomi.Length does not have () because it is a property, not a method.
Your arrays also need to be static.
static string[] nomi = new string[7];
static string[] cognomi = new string[7];
Just to clarify, you don't always need to use static when declaring variables. The reason you need it here is because you're calling them from within a static method (static void Main(string[] args)).
Edit - As mentioned in the comments, this assumes that the variables are being declated outside the Main method which is resulting in the error An object reference is required for the nonstatic field, method, or property 'member'. If they are not, then these variables do not have to be static.
Your for loops don't make any sense for (i = 0; i = 6; i++)
The syntax is for (initialization, condition, iteration)
Right now your condition is an assignment, so it is always going to be true. I think you wanted it to be for (i = 0; i **<** 6; i++)
When writing for loops you should always make a int variable in the scope of the declaration of the for loop
Example:
for (int i = 0; i < 10; i++)
{
body code goes here
}
There integer should be declared and disappear within the scope of the loop.
Hope that helps
I corrected your for and Console.WriteLine syntax and made your code more readable. This should be able to guide you.
const int length = 7;
string[] nomi = new string[length];
string[] cognomi = new string[length];
for (int i = 0; i < length; i++)
{
Console.WriteLine("Inserisci il {0} ° nome", i + 1);
nomi[i] = (Console.ReadLine());
}
for (int i = 0; i < length; i++)
{
Console.WriteLine("Inserisci il {0} ° cognome", i + 1);
cognomi[i] = (Console.ReadLine());
}
for (int i = 0; i < length; i++)
{
Console.WriteLine("nomi: {0} cogomi: {1}", nomi[i], cognomi[i]);
}

Categories