Run same code multiple times in parallel with different parameter - c#

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());

Related

Is there a way to reuse the same thread?

var a = 3
var b= 5
for (int i = 0; i < 10; i++)
{
var c = a + b;
Console.WriteLine(c); //This line should execute on another thread
}
How could I make the value of c print on another thread, without making a new thread each iteration? The reason I want to make it run on another thread is because writing the value of c would block the thread and make the script slower. Basically its for visual feedback
Note: I cant put a loop in the thread since the thread is only supposed to run after c is calculated
I think you need a standard Producer/Consumer pattern.
using System.Threading.Channels;
Channel<int> channel = Channel.CreateUnbounded<int>();
ChannelWriter<int> writer = channel.Writer;
ChannelReader<int> reader = channel.Reader;
var task = Task.Run(async () => await Consume());
var a = 3;
var b = 5;
for (int i = 0; i < 10; i++)
{
var c = a + b + i;
await writer.WriteAsync(c);
}
writer.Complete();
await task; // or Console.ReadKey();
async Task Consume()
{
await foreach (int x in reader.ReadAllAsync())
{
Console.WriteLine(x);
}
}
The part of the code that publishes data to the channel can also be executed in a separate thread, if necessary.
Based on the information you have given, a possible solution would be the following:
int sum = 0;
object lockObj = new object();
var tokenSource2 = new CancellationTokenSource();
CancellationToken ct = tokenSource2.Token;
void Progress()
{
while (true)
{
// We should lock only if writting to the sum var but this is just an example.
lock (lockObj)
{
Console.WriteLine(sum);
}
// Just simple wait in order to not write constantly to the console.
Thread.Sleep(100);
// If process canceled (finished), then exit.
if (ct.IsCancellationRequested)
return;
}
}
var a = 3;
var b = 5;
Task? t = null;
for (int i = 0; i < 100000; i++)
{
// Lock to be sure that there is no concurrency issue with the Progress method if it writes to the variable.
// In our case it shouldn't be required as it only reads.
lock (lockObj)
{
sum = a + b;
}
if (t == null)
t = Task.Run(() => Progress());
}
tokenSource2.Cancel();
t.Wait();
Things to note:
We lock the access of the sum variable although it is not required as we only read from it. You can safely remove the lock statements if you do not intent to write to it from the background thread.
We use a cancellation token to indicate the termination of the thread that does the calculation (in our case the main thread). This way the other thread will terminate correctly and it will not end up running an endless loop.
We use a Task.Wait() after we end our calculation. If we don't do it, the console app will instantly kill the background thread and we might not get results displayed on the screen.

My Thread Function Runs with doubled arguments

I'm making thread on foreach loop.
I give array value and count for the threads, and want to see the list.
But my thread[] is running with same count argument, randomly.
Also, T[0] doesn't get terminated normally. I guess this is with argu overlapping problem too..
This makes the result panel to be placed on other panels.
Thread[] T = new Thread[VA.Count];
int count = 0;
ThreadEnd = new CountdownEvent(VA.Count);
foreach (var item in VA)
{
T[count] = new Thread(delegate () { SetResultBox(count, item); });
T[count].Start();
count++;
}
ThreadEnd.Wait();
private void SetResultBox(int RunCount, JToken item)
{
VideoJson videoinfo = new VideoJson();
videoinfo.title = item["snippet"]["title"].ToString();
videoinfo.description = item["snippet"]["description"].ToString();
videoinfo.ThumbnailURL = item["snippet"]["url"].ToString();
VideoArray.Add(videoinfo);
SearchResultControl SRC = new SearchResultControl(videoinfo);
SRC.Location = new Point(0, RunCount * 110);
ResultControlList.Add(SRC);
ThreadEnd.Signal();
}
I want to know why SetResultBox function's argument is getting overlapped.
Important thing is that I hope there are no Join method.
If VA array gets bigger, this function works too slow with Join Func..
You are implicitly capturing the context of count thus making the thread to use the value of count which is present at the time thread actually starts doing the job. I guess the foreach loop manages to iterate faster than the Thread actually starts. Declare a local variable which would make you a copy of the value as it is at the moment and it should run fine:
foreach (var item in VA)
{
var currentCount = count;
T[count] = new Thread(delegate () { SetResultBox(currentCount, item); });
T[count].Start();
count++;
}
This is very common problem, You need to create a dedicated variable for count to pass into the delegate / lambda
foreach (var item in VA)
{
var count1 = count;
T[count] = new Thread(delegate () { SetResultBox(count1, item); });
T[count].Start();
count++;
}

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();
}

Pass non reference value to Task

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];
});
}

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.

Categories

Resources