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