ArgumentOutOfRangeException. But it should not be there - c#

So I have and method like this.
var someColletion = _someService.GetSomeCollection(someParam);
var taskCollection = new Task<double>[someCollection.Count];
for (int i = 0; i < taskCollection.Length; i++)
{
// do some stuff on the i-th element of someCollection and taskCollection
// and start the i-th task
}
Task.WaitAll(taskCollection);
double total = 0;
for (int i = 0; i < taskCollection.Length; i++)
{
// get the result of each task and sum it in total variable
}
return total;
the case is when it comes into first for loop and the number of elements in both collections are suppose 1 the ArgumentOutOfRangeException is being thrown and then AggregateException is being thrown on Task.WaitAll() because the i becomes 1 (I don't know why but it does) and when it tries to access the i-th (second) element in array that contains just one element, this happens. But there is more to this. If i set a break point before first loop and go step by step then this thing does not happen. when i becomes one the cycle ends. and everything's okay. now the method I provided above is called by an ASP.NET MVC Controller's Action which itself is called Asynchronously (by ajax call) suppose 3 times. and out of this three just one executes correctly other two do the thing I said above (if not breakpointed). I think that this problem is caused by ajax call most probably because when I breakpoint it stops other calls from executing. Can anyone suggest anything ?

I suspect you're using i within the first loop, capturing it with a lambda expression or anonymous method, like this:
for (int i = 0; i < taskCollection.Length; i++)
{
taskCollection[i] = Task.Run(() => Console.WriteLine(i));
}
If that's the case, it's the variable i which is being captured - not the value of the variable for that iteration of the loop. So by the time the task actually executes, it's likely that the value of i has changed. The solution is to take a copy of the iteration variable within the loop, in a separate "new" variable, and capture that in the anonymous function instead:
for (int i = 0; i < taskCollection.Length; i++)
{
int copy = i;
taskCollection[i] = Task.Run(() => Console.WriteLine(copy));
}
That way, each task captures a separate variable, whose value never changes.

Related

Task.Factory.StartNew() generating object not initialzed error

I have this simple function,working as a task, that only print the values of a dataset. I pass the dataset from main function, and the index. The problem is that i have populated only 2 dataset index, however the function always jumps one ahead, i.e. in the last iteration it would want to start reading index 2, which is uninitialized and therefore the exception.
for (int i = 0; i < 2; i++)
{
tasks.Add(Task.Factory.StartNew(() => {
int a = i;
showNodeID(dataSet,a);
}));
}
and the function is
private static void showNodeID(DataSet[] ds, int a)
{
Console.WriteLine(a.ToString());
Console.WriteLine(ds[a].GetXml());
} //END
In the last iteration when i print 1 however in function if i print a it would be 2.
I assume you are aware of the dangers of captured counter variables in lambda closures, since you attempt to avoid the issue by assigning the counter to a locally-scoped variable. However, your assignment is happening too late – by the time the task starts and copies the value, the counter might already have been incremented by the next iteration. To properly avoid the issue, you need to copy the value before the task, not within it:
for (int i = 0; i < 2; i++)
{
int a = i;
tasks.Add(Task.Factory.StartNew(() =>
{
showNodeID(dataSet, a);
}));
}
If you just need to perform a parallel loop, you could alternatively use:
Parallel.For(0, 2, i => showNodeID(dataSet, i));

While debugging, how can I start a foreach loop at an arbitrary position in the enumeration?

I am debugging my program. Can I set a start object for the foreach loop in the debug mode? For instance, I want the foreach loop to start from the 5th element of my collection.
No, you cant. Foreach loop uses IEnumerable<T> where T is the type of object. So unlike for loop you cannot set initial or start index for the iteration. So you will always start from the 0th location object.
Other option is to use linq as shown below.
//Add using System.Linq statement at top.
int[] numbers = new int[] { 1,2,3,4,5,6,7,8};
foreach(var num in numbers.Skip(5))
{
Console.WriteLine(num);
}
Considering you are talking about debugging I'm assuming what you want to do is to break from the 5th element on etc. If this is the case then you can use a break point specifying a hit count which will break on the nth hit and so on.
See MSDN here.
An easy way to set the start object is to overwrite the variable that you are enumerating in the loop.
To illustrate, consider the following foreach loop.
IEnumerable<int> numbers = Enumerable.Range(1, 100);
foreach (int n in numbers)
{
Console.WriteLine(n);
}
Set your breakpoint on the numbers statement in the loop initializer, or step to the loop but don't run the numbers statement.
Use the immediate window to override the value of numbers:
numbers = numbers.Skip(5); // or Enumerable.Skip(numbers, 5)
Continue debugging; loop runs from the sixth element.
If your loop uses an inline-computed enumeration as follows, then you're out of luck. Consider using a hit-count breakpoint instead.
foreach (int n in Enumerable.Range(1, 100)) // no way to change enumeration.
{
Console.WriteLine(n);
}
Note: once the numbers statement is run, the debugger will cache your enumeration so it may no longer be changed and affect the loop. You may observe this while stepping through the loop construct.
foreach does not necessarily operate on a collection which does even have a defined order. consider a Dictionary<T> or a ConcurrentBag<T>.
so, no, this is not possible in the general case, especially without code changes (which could change the problem you are trying to debug).
#Shaggy's answer is probably the easiest, but it's good to know that the old fashioned for loop is the fastest, and enables you to skip the 0 index.
for(int x = 4; x < numbers.Length; x++)
{
// Do anything with numbers[x]
}
Even though I would not recommend it (It's really bad code), you could make this loop behave different when debugging, as you asked:
#if DEBUG
for(int x = 4; x < numbers.Length; x++)
#else
for(int x = 0; x < numbers.Length; x++)
#endif
{
// Do anything with numbers[x]
}

Confusing thread behaviour [duplicate]

This question already has answers here:
creating new threads in a loop
(2 answers)
Closed 9 years ago.
In C#, if I execute
for (int i = 0;i < 10;i++)
new Thread(() => Console.Write(i)).Start();
I will possibly get 0223557799, that's strange, since i is a int, I think it should be copied before the thread starts.
Closures are your problem here.
Basically, instead of grabbing the value when you create the lambda (in the loop), it grabs it when it needs it. And computers are so fast that by the time that happens, it's already changed. It can't go through the whole loop, but it goes through some of it.
Here's a fix:
for (int i = 0; i < 10; i++)
{
var n = i;
new Thread(() => Console.Write(n)).Start();
}
Because Start() returns immediately, i++ happens before the thread gets a chance to print i to the console. I believe that a workaround is to create a local copy of the int, then print that:
for (int i = 0;i < 10;i++) {
int j = i;
new Thread(() => Console.Write(j)).Start();
}
What basically is happening is this:
You want to start a thread that prints the value of i.
The thread starts.
The code operating in the thread gets the value if i. Note that the value of i can be changed by now.
The value of i gets printed. But no guarantees to get a logical output.
Copy the value of i into another variable first and then print that value. The other answers provide enough samplecode.
Your lambda will be translated into set of the method and context class which will handle a refference to the i.
I would use the built in .NET parallelism support Task Parallelism
You won't have to worry about managing the threads it's done for you.
Example your code converted to the Parallelism libraries.
Parallel.For(0, 10, (i, state) =>
{
Console.WriteLine(i);
});

.NET 4.5 parallel processing and for loop

I am trying to create a list of tasks which depend on the number of processors available. I have a for loop which which seems to be behaving strangely. I am aware of the concept of closures in javascript, and it seems like something similar could be happening here:
var tasks = new Task[Environment.ProcessorCount];
for(int x = 0; x < Environment.ProcessorCount; x ++)
{
tasks[x] = Task.Run(() => new Segment(SizeOfSegment, x * SizeOfSegment, listOfNumbers).generateNewList());
}
What I am finding is when I break on the line in the for loop, the variable x seems to be correct, so it starts at 0 and ends at 3 (number of processors is 4). But when I put the break point within the constructor for Segment, I am finding that x was actually 4 when stepping back in the Call Stack.
Any help would be greatly appreciated.
You're capturing x within your lambda expression - but you've got a single x variable which changes values across the course of the loop, so by the time your task actually runs, it may well have a different value. You need to create a copy of the variable inside the loop, creating a new "variable instance" on each iteration. Then you can capture that variable safely:
for(int x = 0; x < Environment.ProcessorCount; x ++)
{
int copy = x;
tasks[x] = Task.Run(() => new Segment(SizeOfSegment,
copy * SizeOfSegment,
listOfNumbers).generateNewList());
}
(I'd also advise you to rename generateNewList to GenerateNewList to comply with .NET naming conventions.)

Creating an Array of Threads in C#

I have a very strange problem with my code. It will fully run the 1st for loop, then complete the foreach, but then it will skip back to the "ThreadStart IMAPDelegate" (line 1 of the for loop) and then crash because of an ArgumentOutOfRangeException. Can someone explain why the program is doing this? I debugged it line by line and it literally just skips back up into the a line in the for loop. If it had normally run the for loop again, it would have set x back to 0 and it would not have crashed. Any suggestions?
for (int x = 0; x < UserInfo.Count; x++)
{
ThreadStart IMAPDelegate = delegate{SendParams(UserInfo[x], IMAPServers[x]); };
MyThreads.Add(new Thread(IMAPDelegate));
}
foreach (Thread thread in MyThreads)
{
thread.Start();
}
This is by design when you use an anonymous method like that. As soon as the thread starts running, it executes the SendParams() method call. Which then bombs because the "x" variable is already incremented beyond UserInfo.Count. Fix:
for (int x = 0; x < UserInfo.Count; x++)
{
int user = x;
ThreadStart IMAPDelegate = delegate{SendParams(UserInfo[user], IMAPServers[user]); };
MyThreads.Add(new Thread(IMAPDelegate));
}

Categories