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
Related
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.
I am defining a jagged array of threads (such that each thread can operate on the directory tree of its own) in this manner
Task[][] threads = new Task[InstancesDir.Length][];
for (int i = 0; i < InstancesDir.Length; i++)
{
threads[i] = new Task[InstancesDir[i].Length];
}
for (int i = 0; i < FilesDir.Length; i++)
{
for (int j = 0; j < FilesDir[i].Length; j++)
{
threads[i][j] = Task.Run(() =>
{
Calculate(i, j, InstancesDir, FilesDir, PointSum);
});
}
Task.WaitAll(threads[i]);
}
But in calculate i always get value of j >= FilesDir[i].Length . I have also checked that objects are passed by value except arrays. What could be a workaround for this and what could be the reason for this behavior?
PS. Introducing a shared lock might help in mitigation the concurrency issue but i want to know about the reason for such behavior.
But in calculate i always get value of j >= FilesDir[i].Length
This isn't a concurrency issue, as your for loop is executing on a single thread. This happens because the lambda expression is closing over your i and j variables. This effect is called Closure.
In order to avoid it, create a temp copy before passing both variables to Task.Run:
var tempJ = j;
var tempI = i;
threads[tempI][tempJ] = Task.Run(() =>
{
Calculate(tempI, tempJ, InstancesDir, FilesDir, PointSum);
});
The following code does not work as I expect it. What am I doing wrong? Output is different on every run. Is there a better way of doing this? Assume action does something more complex than what's below.
Action<int> action = (int m) =>
{
if ((m % 2) == 0)
Console.WriteLine("Even");
else
Console.WriteLine("Odd");
};
const int n = 10;
Task[] tasks = new Task[n];
for (int i = 0; i < n; i++)
{
tasks[i] = Task.Factory.StartNew(() => action(i+1));
}
Task.WaitAll(tasks);
The lambda in your loop is capturing a reference to the same i variable every time through the loop, not its value.
Change your loop to something like:
for (int i = 0; i < n; i++)
{
var j = i;
tasks[i] = Task.Factory.StartNew(() => action(j+1));
}
Note that the output will still be different on every run, but you should get exactly five even and five odd outputs.
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]));
}
i have this code:
Thread[] threadsArray = new Thread[4];
for (int i = 0; i < 4; i++)
{
threadsArray[i] = new Thread(() => c1.k(i));
}
for (int i = 0; i < 4; i++)
{
threadsArray[i].Start();
}
for (int i = 0; i < 4; i++)
{
threadsArray[i].Join();
}
the function k is this:
void k(int i)
{
while(true)
Console.WriteLine(i);
}
for some reason just the last thread is running and printing 4444444....
why aren't all the threads running?
All of the threads are printing the same variable.
Your lambda expression (() => c1.k(i)) captures the i variable by reference.
Therefore, when the lambda expression runs after i++, it picks up the new value of i.
To fix this, you need to declare a separate variable inside the loop so that each lambda gets its own variable, like this:
for (int i = 0; i < 4; i++)
{
int localNum = i;
threadsArray[i] = new Thread(() => c1.k(localNum));
}
You are closing over the i variable.
Try this instead
for (int i = 0; i < 4; i++)
{
int x = i;
threadsArray[i] = new Thread(() => c1.k(x));
}
Thread[] threadsArray = new Thread[4];
for (int i = 0; i < 4; i++)
{
//better use ParameterizedThreadStart Delegate
threadsArray[i] = new Thread(k);
}
for (int i = 0; i < 4; i++)
{
//passing the value to the function
threadsArray[i].Start(i);
}
for (int i = 0; i < 4; i++)
{
threadsArray[i].Join();
}
//changing the input data type
void k(object i)
{
while (true)
Console.WriteLine((int)i);
}