Pass non reference value to Task - c#

I often get strange resulst when passing int variables to tasks such as in this example:
List<List<object>> ListToProcess = new List<List<object>>();
// place some lists in list to process
foreach (var temp in Foo)
ListToProcess.Add(temp);
foreach (var tempArray in ListToProcess)
{
// initialize each list in ListToProcess
}
int numberOfChunks = ListToProcess.Count;
Task[] tasks = new Task[numberOfChunks];
for (int counter = 0; counter < numberOfChunks; counter++)
{
tasks[counter] = Task.Factory.StartNew(() =>
{
// counter is always = 5 why? <---------------------------
var t = ListToProcess[counter];
});
}
How can I solve this problem?

This is known as a closure. You are not using the value of the variable, you are using the variable itself. When the code executes, it uses the value at the time of execution, not the value when the Task was defined.
To fix this issue, I believe you would do something like this:
for (int counter = 0; counter < numberOfChunks; counter++)
{
int cur = counter;
tasks[counter] = Task.Factory.StartNew(() =>
{
// counter is always = 5 why? <---------------------------
var t = ListToProcess[cur];
});
}

There is no guarantee as to when the 'counter' variable in the Action block of StartNew will be accessed. What is likely to happen is that all 5 values are looped through, and the tasks are created, then the tasks are scheduled to run.
When they do run, the following is executed:
var t = ListToProcess[counter];
But at this stage count is already equal to 5.
Perhaps you should look at parallel collections?
ListToProcess.AsParallel().ForAll(list => dosomething(list));
There are many other options around this area.

for (int counter = 0; counter < numberOfChunks; counter++)
{
var referenceVariable = new{val=counter};
tasks[counter] = Task.Factory.StartNew(() =>
{
var t = ListToProcess[referenceVariable.val];
});
}

Since variables are captured, you can solve this by redeclaring a new variable in each loop.
for (int counter = 0; counter < numberOfChunks; counter++)
{
int localCounter = counter;
tasks[localCounter] = Task.Factory.StartNew(() =>
{
// counter is always = 5 why? <---------------------------
var t = ListToProcess[localCounter];
});
}

Related

Run same code multiple times in parallel with different parameter

This very simple example:
int numLanes = 8;
var tasks = new List<Task>();
for (var i = 0; i < numLanes; ++i)
{
var t = new Task(() =>
{
Console.WriteLine($"Lane {i}");
});
tasks.Add(t);
}
tasks.ForEach((t) => t.Start());
Task.WaitAll(tasks.ToArray());
Produces:
Lane 8
Lane 8
Lane 8
Lane 8
Lane 8
Lane 8
Lane 8
Lane 8
Which is not as expected, the parameter i isn't passed correctly. I had thought to use Action<int> to wrap the code but couldn't see how I would. I do not want to write a dedicated method like Task CreateTask(int i) I'm interested how to do it using lambdas.
What is normal way to do this - spin up the same code a bunch of times in parallel with a different parameter value?
You've got a captured loop variable i, try to add temp variable inside a loop and pass it to the Task
for (var i = 0; i < numLanes; ++i)
{
var temp = i;
var t = new Task(() =>
{
Console.WriteLine($"Lane {temp}");
});
tasks.Add(t);
}
Further reading How to capture a variable in C# and not to shoot yourself in the foot. foreach loop has the same behavior before C# 5, but according to link above
with the release of the C# 5.0 standard this behavior was changed by
declaring the iterator variable inside every loop iteration, not
before it on the compilation stage, but for all other constructions
similar behavior remained without any changes
So, you may use foreach without temp variable
You need to capture the value inside the for loop otherwise all of the Tasks are still referring to the same object:
for (var i = 0; i < numLanes; ++i)
{
var innerI = I; // Copy the variable here
var t = new Task(() =>
{
Console.WriteLine($"Lane {innerI}");
});
tasks.Add(t);
}
See here for more info.
You could use LINQ to create a closure for each lambda you pass to the Task constructor:
var tasks = Enumerable.Range(0, numLanes - 1)
.Select(i => new Task(() => Console.WriteLine($"Lane {i}")));
Another approach (without introducing additional variable inside for loop) is to use constructor Task(Action<object>, object):
int numLanes = 8;
var tasks = new List<Task>();
for (int i = 0; i < numLanes; ++i)
{
// Variable "i" is passed as an argument into Task constructor.
var t = new Task(arg =>
{
Console.WriteLine("Lane {0}", arg);
}, i);
tasks.Add(t);
}
tasks.ForEach((t) => t.Start());
Task.WaitAll(tasks.ToArray());
In C# 5 and later foreach loop introduces a new variable on each iteration. Therefore in C# 5 and later it is possible to use foreach to create tasks where each task captures its own loop variable (also no need to introduce additional variable inside loop):
int numLanes = 8;
var tasks = new List<Task>();
foreach (int i in Enumerable.Range(0, numLanes))
{
// A new "i" variable is introduced on each iteration.
// Therefore each task captures its own variable.
var t = new Task(() =>
{
Console.WriteLine("Lane {0}", i);
});
tasks.Add(t);
}
tasks.ForEach((t) => t.Start());
Task.WaitAll(tasks.ToArray());

How to prevent "index" to updated itself in array of Action

When I add to array of Actions with a for loop a delegate the I is updated across the array. How to prevent that from happening?
I have tried assigning the "I" to an integer before adding that works.
Action[] actions = new Action[100];
for (int i = 0;i< actions.Length; i++)
{
actions[i] = () => Console.WriteLine("Hello"+ i);
}
"I" in each Action in Action[] is 100;
Why is that?
because they are all assigned to the same local variable "int i"
and after the loop end "i" is 100
Action[] actions = new Action[100];
for (int i = 0;i< actions.Length; i++)
{
int a = i;
actions[i] = () => Console.WriteLine("Hello"+ a);
}
after declare int a = i , you have respective a for each actions
Here and Here are good explanations on similar question. Here is also good explanation on C# closures by Jon Skeet.
In for loop there is only one single variable i used. That's why later on when you are executing actions, they all reference to same value i=100. If action need to use actual value of the current i you have to capture a copy of it and store copy to action.
for (int i = 0;i< actions.Length; i++)
{
int copy = i;
actions[i] = () => Console.WriteLine("Hello"+ copy);
}

how to capture value returned from method in thread execution c#

Thread[] threads = new Thread[12];
int temp;
for (int i = 0; i < threads.Length - 1; i++)
{
temp = i;
threads[temp] = new Thread(new ThreadStart(()=> test(test1[temp],"start", temp)));
threads[temp].Start();
//threads[temp].Join();
}
for(int i=0; i<threads.Length-1; i++)
{
threads[i].Join();
}
//Need to capture the response returned from method executed"test1" in thread.
You could use a Task<T> (if you're on .NET 4+), which has a return value. You could also use events to get notified when the thread is done with doing whatever it does and get the returned value that way.
I would use Microsoft's Reactive Framework for this. NugGet "Rx-Main".
var query =
Observable
.Range(0, 12)
.SelectMany(n => Observable
.Start(() => new
{
n,
r = test(test1[n], "start", n)
}))
.ToArray()
.Select(xs => xs
.OrderBy(x => x.n)
.Select(x => x.r)
.ToArray());
query.Subscribe(rs =>
{
/* do something with the results */
});
You could start the thread using another ctor overload where you can start the thread and pass an object to that thread. The thread would then save the result in a field of that object. The main thread could after the call to Join retrieve the results from all those objects. You could have an array of 12 objects each of them passed to one thread. Or you could have an array of 12 classes, each class encapsulating one thread and the corresponding object that wraps the result:
public class ThreadResult
{
public int Result {get; set;}
}
However, today you have better choices than raw threads. Take a look at TPL (Task Parallel Library) and async / await in C#.
You can also use a shared state, in this case you have to lock every access to the shared objects inside the thread method:
Thread[] threads = new Thread[12];
int temp;
string msg = "";
List<string> results = new List<string>();
for (int i = 0; i < threads.Length; i++)
{
temp = i;
threads[temp] = new Thread(() =>
{
lock (results)
{
lock (msg)
{
msg = "Hello from Thread " + Thread.CurrentThread.ManagedThreadId;
results.Add(msg);
}
}
});
threads[temp].Start();
}
for (int i = 0; i < threads.Length; i++)
{
threads[i].Join();
}

Task.Start fails when continuation actions are not inline

What is the wrong the with following code?
Task.Start fails with index out of range exception. To be more clear.. it is failing because the i value is coming as 3 in the for loop !!!
ActionProvider m1 = new ActionProvider();
ActionProvider m2 = new ActionProvider();
ActionProvider m3 = new ActionProvider();
List<Action> actions = new List<Action>()
{
()=> { m2.DoIt(); },
()=> { m3.DoIt(); },
};
Task t = new Task(() => { m1.DoIt(); });
for (int i = 0; i < actions.Count; i++)
{
t.ContinueWith(t1 => actions[i]());
}
t.Start();
It is probably because you reuse the same variable, i, several times. So when you execute the task i has been incremented.
Try to change your for-loop as follows:
for (int i = 0; i < actions.Count; i++)
{
var action = actions[i];
t.ContinueWith(t1 => action());
}
The only difference here is that I create a copy of the variable that I pass to ContinueWith.

C# using loop to start threads and pass parameters

In below sample code, I use lambda function to make 3 threads doing different things. My goal is make the thread count configurable, so I was thinking using a loop to start threads. But I always got in static function can't call non-static members error. Can the community help me or direct me to a tutorial? Thanks a lot!
My Code:
internal class FeedClient
{
private static void Main(string[] args)
{
int iteration = 10;
int ranSleepTime = 1000;
var obj = new MyClass();
var threads = new Thread[3];
(threads[0] = new Thread(() =>
{
Random random = new System.Random();
for (int i = 0; i < iteration; i++)
{
obj.MyMethod("my string 1");
Thread.Sleep(random.Next(ranSleepTime));
}
})).Start();
(threads[1] = new Thread(() =>
{
Random random = new System.Random();
for (int i = 0; i < iteration; i++)
{
obj.MyMethod("my string 2");
Thread.Sleep(random.Next(ranSleepTime));
}
})).Start();
(threads[2] = new Thread(() =>
{
Random random = new System.Random();
for (int i = 0; i < iteration; i++)
{
obj.MyMethod("my string 3");
Thread.Sleep(random.Next(ranSleepTime));
}
})).Start();
foreach (Thread thread in threads)
{
thread.Join();
}
obj.Close(false);
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
Desired look:
for(int i=0;i<3;i++){
threads[i] = new Thread(func); // func is the lambda function
threads[i].Start(myData[i]); // myData[] may be a string array
}
The error message seems to indicate that you are attempting to use an instance member from a static method somewhere. Naturally that is not allowed since a static method does not have a this reference. Here is how I would refactor your code.
public static void Main()
{
string[] myData = GetStringArray();
int iteration = 10;
int ranSleepTime = 1000;
var obj = new MyClass();
var threads = new Thread[myData.Length];
for (int i = 0; i < threads.Length; i++)
{
int captured = i; // This is required to avoid capturing the loop variable.
threads[i] = new Thread(
() =>
{
var random = new Random();
for (int i = 0; i < iteration; i++)
{
obj.MyMethod(myData[captured]);
Thread.Sleep(random.Next(ranSleepTime));
}
});
threads[i].Start();
}
foreach (Thread thread in threads)
{
thread.Join();
}
obj.Close(false);
}
I must mention, however, that creating new threads in an unbounded loop is generally undesirable. If the loop has a tight bound then maybe, but I would have to get a better understanding of the problem before making any further comments regarding this point.

Categories

Resources