Tasks in array -- only last one runs - c#

I was experimenting with tasks. Why does this output 10 and not each value of the loop?
public static void StartTasks()
{
Task[] tasks = new Task[10];
for (int i = 0; i < 10; i++)
tasks[i] = new Task(() => Console.WriteLine(i));
foreach (Task task in tasks)
{
task.Start();
}
}

C# lambdas capture a reference to the variable, not the value of the variable.
If you want to capture the value, you need to make a copy of it first inside the loop which causes the capture to get the reference to the locally scoped unchanging variable.
public static void StartTasks()
{
Task[] tasks = new Task[10];
for (int i = 0; i < 10; i++) {
int j = i;
tasks[i] = new Task(() => Console.WriteLine(j));
}
foreach (Task task in tasks)
{
task.Start();
}
}

In addition to the accepted answer, you can also pass a parameter to the task. For example,
using System;
using System.Threading.Tasks;
static void StartTasks(int instances)
{
var tasks = new Task[instances];
for (int i = 0; i < instances; i++)
{
tasks[i] = new Task((object param) =>
{
var t = (int)param;
Console.Write("({0})", t);
}, i);
}
Parallel.ForEach<Task>(tasks, (t) => { t.Start(); });
Task.WaitAll(tasks);
}

Related

Running multiple tasks at the same time using a single source data

So how can I run multiple tasks at once using a txt file as input.
Load Source Data
var lines = File.ReadAllLines("file.txt")
Run Tasks
forearch(var line in lines)
{
//I want to execute 3 tasks and each task needs to receive a line. At the end of each task he should pick up another line that has not been used and continue to the end of the file.
}
Have you looked at Parallel.ForEach?
use like :
Parallel.ForEach(File.ReadLines("file.txt"), new ParallelOptions { MaxDegreeOfParallelism = 3 }, line => { \\ do stuff })
Maybe something like this:
async void Main()
{
var lines = File.ReadAllLines("file.txt");
int i = 0;
var concurrency = 3;
while (i < lines.Length)
{
var tasks = new List<Task>(concurrency);
for (int j = 0; j < concurrency && i < lines.Length; j++)
{
tasks.Add(MyMethod(lines[i++]));
}
await Task.WhenAll(tasks);
}
}
public Task MyMethod(string s)
{
return Task.CompletedTask;
}
you can try this:
private static async Task Main(string[] args) {
const ushort concurrentWorkers = 5;
var lines = File.ReadAllLines("file.txt");
var concurrentSourceQueue = new ConcurrentQueue<string>(lines);
var worker = Enumerable.Range(0, concurrentWorkers)
.Select(_ => DoWorkAsync(concurrentSourceQueue));
await Task.WhenAll(worker);
}
private static async Task DoWorkAsync(ConcurrentQueue<string> queue) {
while (queue.TryDequeue(out var item)) {
//process line here
}
}

Console.Writeline is only writing to the console some of the times when using async await

So, I have a super simple application, but as I am testing this out it is only writing to the console from the method DoWork(). I am unsure why that is, but I am fairly sure it has to do with the fact that it is async code. Any ideas, why it only writes from method DoWork()?
class Program
{
static void Main(string[] args)
{
MainAsync().Wait();
System.Threading.Thread.Sleep(50000);
}
static async Task MainAsync()
{
Console.WriteLine("Hello World!");
for (int i = 0; i < 300; i++)
{
List<Task> myWork = new List<Task>();
myWork.Add(DoWork(i));
if (myWork.Count == 50)
{
await Task.WhenAll(myWork);
Console.WriteLine("before delay");
await Task.Delay(1000);
Console.WriteLine("after delay");
myWork.Clear();
Console.WriteLine("List cleared.");
}
}
}
public static async Task DoWork(int i)
{
await Task.Delay(0);
Console.WriteLine("Run: " + i);
}
}
You're creating a new List for each iteration of the loop...it will only ever have one thing in it.
Declare the List outside of the loop.
Change:
for (int i = 0; i < 300; i++)
{
List<Task> myWork = new List<Task>();
To:
List<Task> myWork = new List<Task>();
for (int i = 0; i < 300; i++)
{

get all processes in parallel

I'm trying to get the CPU% for all processes in parallel using C#'s TPL. The code that I have is:
private IDictionary<Process, int> _usage = new Dictionary<Process, int>();
public ProcessCpuUsageGetter()
{
Process[] processes = Process.GetProcesses();
int processCount = processes.Count();
Task[] tasks = new Task[processCount];
int counter = 0;
for (int i = 0; i < processCount; i++)
{
tasks[i] = Task.Factory.StartNew(() => DoWork(processes[i]));
}
Task.WaitAll(tasks);
}
private void DoWork(object o)
{
Process process = (Process)o;
PerformanceCounter pc = new PerformanceCounter("Process", "% Processor Time", process.ProcessName, true);
pc.NextValue();
Thread.Sleep(1000);
int cpuPercent = (int)pc.NextValue() / Environment.ProcessorCount;
_usage.Add(process, cpuPercent);
}
But it fails with An item with the same key has already been added. Any ideas on what I'm doing wrong?
The problem is the closure of the local variable i when passed to the expression for starting the task. This causes current value of i used by the DoWork(processes[i]) even when i being modified by the for.
Create a local variable:
for (int i = 0; i < processCount; i++)
{
int localI = i;
tasks[i] = Task.Factory.StartNew(() => DoWork(processes[localI]));
}

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