Threads and IndexOutOfRange exception - c#

As you can clearly see the highest index of the args[] is 2, however the iterator somehow gets to 3.
Explanation?
Edit: The commented Thread.Sleep magically fixes the problem.

This is caused by i being declared outside of the for loop, for the entire for loop. Since there is no guarantee that the Thread is executed at that point, the value of i can change before the Thread executes. You can "resolve" this by declaring a local variable in the for loop.
//for loop ..
var localIndex = i;
var temp = new Thread(() => PrintOut(args[localIndex], IsFilePath(args[localIndex])));
temp.Start();
//for loop ..
EDIT: Also, can you please post a code snippet next time, saves me having to write out the code again :P

First of all
for (var i = 0; i< args.Length; i++)
{
}
is equivalent to:
int i = 0;
loop:
if (i < args.Length)
{
i++;
goto loop;
}
so you see that i is incremented to 3 in order to check your condition.
Of course, new Thread(() => PrintOut(args[i], IsFilePath(args[i]))) is never actually called when i is 3. However, since there is only one instance of i which the loop changes on each iteration, the execution that started when i was 2 is affected when i is incremented to 3.
To fix this, you need to copy the value of i into a new variable on each loop iteration. This copy will not be updated by the for loop:
for (var i = 0; i< args.Length; i++)
{
var copy = i;
Thread temp = new Thread(() => PrintOut(args[copy], IsFilePath(args[copy]))
}

Related

I created a for loop and it doesnt work, but the while loop does, any sugestions?

Why doesnt this for loop work while the while loop does
for (int i = 0; i > 10; i++)
{
Console.WriteLine(i);
}
int j = 1;
while(j != 11)
{
Console.WriteLine(j);
j++;
}
It's easy to mix up comparers. A good way to remember for me is the heart. <3 because I know that's read as "Less than three".
It's easy to get confused when starting out, you just mixed up the condition.
As of why your for does not work is because it starts with i = 0 then checks if if 0 is greater than 10 which is not that's why it will not excute the loop body and terminates the loop.
In your while loop, initially j = 1 then while checks if 1 is not equal to 11, which is true so loop body will execute until j is not equal to 11.

How does a for loop allow redeclaration of a variable?

This:
int j = 1;
int j = 2;
Console.WriteLine(j.ToString());
.. produces a compile error:
A local variable named 'j' is already defined in this scope
Yet this works fine:
for (int i = 0; i < 10; i++)
{
int j = i;
Console.WriteLine(j.ToString());
}
Why?
How does the loop simultaneous retain values from each iteration whilst being able to redeclare a variable with the same name in the same scope?
The variable j exists only per iteration, i.e. in each iteration a variable j is declared, assigned, used and then discarded and the next iteration begins and the process repeats and so forth. hence you don't get the same compilation error as the first example snippet.
When you write int j=1 and int j=2, you are trying to declare the variable twice (you can only declare it once).
You could, however, overwrite the value of j:
int j = 1; // now j has a value of 1
j = 2; // now j has a value of 2
This is what the for loop is doing - each iteration of the for loop, the value is updated. A new instance of j is not created during each iteration.
In the first example you are defining two variables with the same name, and they exist at the same time.
In the loop, every variable is created in the loop context. After each iteration the variable is destroyed, allowing you to create a new one with the same name(on the next iteration). In other words, on the loop they don't exist at the same time.

c# for loop is entering without meeting its condition

So, I have this for loop:
double spec = 0, tot = 0;
for (int i = 0; i < omega_algo.Length; i++)
{
if (omega_algo[i] > 0)
spec = Math.Sqrt(omega_algo[i]);
else
spec = 0;
tot += spec;
}
Where myArray.Length = 50.
I get an IndexOutOfRangeException while debugging and see that i is 50.
So, the for loop is entering when it shouldn't ( i < myArray.Length is false )!
This exception only occurrs ocasionally, which makes it even more weird.
Does someone have an explanation/fix for this? Am I missing something or could this be a weird Visual Studio bug?
EDIT:
I've edited the for loop to show the code.
No i is being incremented and omega_algo array is not changing at all.
EDIT:
Based on the comments below, I wrote a sample app, and your code should work as is.
If your array really does have a length of 50, then the value of i will never be 50. The only way this would be possible is if you are changing the value of i inside the loop.
Can you provide more code to show some context of how/where this is being used? How the array is being defined etc?
Your code should work if executed on a single thread. Do you have any thread or asynchrone jobs that are editing the array?
If so, just lock the array before accessing it and before editing it.
lock(myArray)
{
for (int i = 0; i < myArray.Length; i++)
{
int someVar = myArray[i]; //this is where exception is thrown when i=50
}
}
EDIT:
Since omega_algo array is not changing at all, this is not a threading issue.

for loop ends prematurely when objects are removed

Hi I have a problem with a for loop.
It looks like this
for (int i = 0; i < ObjectManager.Instance.Objects.Count; i++)
{
if (ObjectManager.Instance.Objects[i] is Asteroid)
{
ObjectManager.Instance.Objects.Remove(ObjectManager.Instance.Objects[i]);
}
}
But the count gets shorter while I remove objects, which causes the loop to end prematurely. Is there a way to do this without a bunch of extra loops.
Why don't you loop backward?
// Just change the order from Count - 1 down to 0
for (int i = ObjectManager.Instance.Objects.Count - 1; i >= 0; --i)
{
if (ObjectManager.Instance.Objects[i] is Asteroid)
{
ObjectManager.Instance.Objects.Remove(ObjectManager.Instance.Objects[i]);
}
}
In case you have to loop forward (e.g. if Instances should be deleted in the order they are created because they are depend on each other) you can modify for loop in this way:
for (int i = 0; i < ObjectManager.Instance.Objects.Count;) // <- No increment here
if (ObjectManager.Instance.Objects[i] is Asteroid)
ObjectManager.Instance.Objects.Remove(ObjectManager.Instance.Objects[i]);
else
i += 1; // <- Increment should be here!
Yet another possibility is Linq:
ObjectManager.Instance.Objects.RemoveAll(item => item is Asteroid);
Three options:
If ObjectManager.Instance.Objects is a List<T>, use List<T>.RemoveAll with a predicate, making your code much simpler:
// This replaces your whole loop...
ObjectManager.Instance.Objects.RemoveAll(x => x is Asteroid);
Count from the end of the collection rather than from the start, so that you don't need to adjust the index afterwards:
for (int i = ObjectManager.Instance.Objects.Count - 1; i >= 0; i--)
Just decrement i after calling Remove, so that you'll look at the right index on the next iteration.
Note that in the second and third options your code will be a lot simpler to read if you extract the expression ObjectManager.Instance.Objects into a local variable before you use it 4 times. Also consider using RemoveAt(i) rather than Remove(instances[i]), assuming RemoveAt is available for the type you're using.

C# Multiple Threads for work then wait until all finished

I'm working on the small SSHClient. I have a list of clients that are connected to different computers. I have a script that I want to run on those computers. I want to run it parallel in different threads.
I got inspired here:
Stackoverflow - threads
Here is my piece of code:
int toProcess, count = 0;
ManualResetEvent resetEvent = new ManualResetEvent(false);
toProcess = count = clients.Count;
for (int i = 0; i < count; i++)
{
new Thread(delegate()
{
var cmd = clients[i].RunCommand("./script.sh");
res += cmd.Result;
if (Interlocked.Decrement(ref toProcess) == 0)
resetEvent.Set();
}).Start();
}
resetEvent.WaitOne();
//do something
To me this code looks OK. But sometimes (actually it's in most cases) it happens that after the program goes correctly out of for loop, it gets correctly to the line resetEvent.WaitOne(); but after, instead of waiting for all threads to finish and continue to proceed the rest of the code, it goes again to new Thread(delegate()... part of the code and since a variable i is already 2(in case there are two clients in the list of clients) I get an error:
Index was out of range. Must be non-negative and less than the size of
the collection.
I wanted to ask how it is possible that it creates another thread although the for loop is finished. And how to avoid that?
Thank you
This is messy, in my opinion. I suggest using Parallel.For instead:
int toProcess, count = 0;
toProcess = count = clients.Count;
object locker = new object();
Parallel.For(0, count, i =>
{
var cmd = clients[i].RunCommand("./script.sh");
lock(locker) res += cmd.Result;
});
See this link: Parallel.For.
You can use a parallel linq query and aggregate its results via Sum method:
var totalResult = (from i in Enumerable.Range(0, client.Count).AsParallel()
let cmd = clients[i].RunCommand("./script.sh")
select cmd.Result).Sum();
With AsParallel method we create as many threads as we can and with Sum method we run the linq query and fetch each result for summing them up

Categories

Resources