For Loop result in Overflow with Task.Run or Task.Start

There was a problem, I hope someone can help me.

I am trying to run 4 Task in Loop, but im getting an ArgumentOutOfRangeException:

for (int i = 0; i < 4; i++) { //start task with current connection tasks[i] = Task<byte[]>.Run(() => GetData(i, plcPool[i])); } 

Loop gets overflow because i = 4

if I run tasks without a loop, they start without problems:

  tasks[0] = Task<byte[]>.Run(() => GetData(0, plcPool[0])); tasks[1] = Task<byte[]>.Run(() => GetData(1, plcPool[1])); tasks[2] = Task<byte[]>.Run(() => GetData(2, plcPool[2])); tasks[3] = Task<byte[]>.Run(() => GetData(3, plcPool[3])); 

Do not know why? GetData Tasks from Siemens PLC via Socket Connection. PLC supports up to 32 connections. I get 200 bytes per connection.

  private byte[] GetData(int id, PLC plc) { switch (id) { case 0: return plc.ReadBytes(DataType.DataBlock, 50, 0, 200); case 1: return plc.ReadBytes(DataType.DataBlock, 50, 200, 200); case 2: return plc.ReadBytes(DataType.DataBlock, 50, 500, 200); case 3: return plc.ReadBytes(DataType.DataBlock, 50, 700, 200); case 4: return plc.ReadBytes(DataType.DataBlock, 50, 900, 117); default: return null; } } 

any idea?

Regards Sam

+8
c # task
source share
2 answers

This is probably due to the closure problem .

Try the following:

  for (int i = 0; i < 4; i++) { //start task with current connection int index = i; tasks[index] = Task<byte[]>.Run(() => GetData(index, plcPool[index])); } 

What is probably happening is that when the last thread started, the loop already increased from i to 4 and that the value is passed to GetData() . Capturing the value of i in a separate variable index , and using it instead should solve this problem.

As an example, if you try this code:

 public static void Main() { Console.WriteLine("Starting."); for (int i = 0; i < 4; ++i) Task.Run(() => Console.WriteLine(i)); Console.WriteLine("Finished. Press <ENTER> to exit."); Console.ReadLine(); } 

he often gives you this conclusion:

 Starting. Finished. Press <ENTER> to exit. 4 4 4 4 

Change this code to:

 public static void Main() { Console.WriteLine("Starting."); for (int i = 0; i < 4; ++i) { int j = i; Task.Run(() => Console.WriteLine(j)); } Console.WriteLine("Finished. Press <ENTER> to exit."); Console.ReadLine(); } 

and you get something like

 Starting. Finished. Press <ENTER> to exit. 0 1 3 2 

Pay attention to how it is NOT MANDATORY to ORDER! You will see all the correct values ​​printed, but in an undefined order. Multithreading is complicated!

+12
source share

In C # 5, a foreach loop variable will be logically inside the loop, and therefore closures will be closed by a new copy of the variable each time . The for loop will not be changed.

Another way:

  • Task creation
  • Run the tasks.
  • Wait for the completion of the entire task.

Demo code:

  internal class Program { private static void Main(string[] args) { Task[] tasks = new Task[4]; for (int i = 0; i < 4; i++) { //start task with current connection tasks[i] = new Task<byte[]>(GetData,i); } foreach (var task in tasks) { task.Start(); } Task.WaitAll(tasks); Console.Read(); } private static byte[] GetData(object index) { var i = (int) index; switch (i) { case 0: //return plc.ReadBytes(DataType.DataBlock, 50, 0, 200); Console.WriteLine(i); return new byte[] { }; case 1: //return plc.ReadBytes(DataType.DataBlock, 50, 200, 200); case 2: Console.WriteLine(i); return new byte[] { }; //return plc.ReadBytes(DataType.DataBlock, 50, 500, 200); case 3: Console.WriteLine(i); return new byte[] { }; //return plc.ReadBytes(DataType.DataBlock, 50, 700, 200); case 4: //return plc.ReadBytes(DataType.DataBlock, 50, 900, 117); Console.WriteLine(i); return new byte[] { }; default: return null; } } } 

OUTPUT:

     3  
     one  
     0  
     2  

Note: it new Task<byte[]>(GetData,i); not new Task<byte[]>(()=>GetData(i));

() => v means "return the current value of the variable v", and not "return the value of v back when the delegate was created." Closing closes over variables, not over values .

Thus, new Task<byte[]>(GetData,i); doesn't have a " Closures Problem "

-one
source share

All Articles