This question already has answers here:
Threading in C#: How to keep loop-index thread-safe?
(2 answers)
Closed 4 years ago.
I have to parallelelize an existing code. Basically, the code takes a file, do a work on it and save the result in a new file. And I need to do this work on a batch of files.
So, I do this example code to view what code structure I need, but it works weirdly:
class Program
{
static void Main(string[] args)
{
Processor processor = null;
ProcessAsync(processor, 3);
}
static void ProcessAsync(Processor processor, int n)
{
IList<Task> tasks = new List<Task>();
for (int i = 0; i < n; ++i)
{
processor = new Processor(i);
tasks.Add(new Task(() => processor.Process()));
tasks[i].Start();
}
for (int i = 0; i < n; ++i)
{
tasks[i].Wait();
}
}
}
class Processor
{
private readonly int id;
public Processor(int id)
{
this.id = id;
}
public void Process()
{
Console.WriteLine(id.ToString("000") + ": " + "Processing...");
Thread.Sleep(1000);
Console.WriteLine(id.ToString("000") + ": " + "Processed!");
}
}
I expect this output:
Task 000: Processing...
Task 001: Processing...
Task 002: Processing...
Task 000: Processed!
Task 001: Processed!
Task 002: Processed!
But I have this result:
Task 002: Processing...
Task 002: Processing...
Task 002: Processing...
Task 002: Processed!
Task 002: Processed!
Task 002: Processed!
Why are all processor's ids 002?
Disregarding other issues, this is a capture and closer problem
You can search for as there are rheams of it written about it on the web, and its just the way the CLR and lambda work
The fix is just create a fresh new local variable
for (int i = 0; i < n; ++i)
{
var proc = new Processor(i);
tasks.Add(new Task(() => proc.Process()));
tasks[i].Start();
}
Looking quickly it looks like it is to do with variable scope and folding scope into the lambda.
Try changing your loop so it is like this:
for (int i = 0; i < n; ++i)
{
tasks.Add(new Task(() => new Processor(i).Process()));
tasks[i].Start();
}
Also, ask if Processor really needs to be a class, could it not be a simple method? There is something odd about the logic where you pass in a Processor, but then create a new one for each loop iteration. Do you really want that?
Related
This question already has an answer here:
Event handler always called with the same value in foreach loop
(1 answer)
Closed 12 months ago.
I have this Code below that runs in a Task. I want my program to more responsive but when I try to run the code below. Some of the task are successfully completed.
private async void SaveRecordToDB()
{
List<Task> listOfTask = new List<Task>();
for (int i = 0; i <= 15; i++)
{
listOfTask.Add(Task.Run(() => IterateRowsSaving(i)));
}
await Task.WhenAll(listOfTask);
}
private bool IterateRowsSaving(int index)
{
bool Success = true;
//Save to Database
//Assuming that every iteration has a long operation running
return Success;
}
What could have been that I have done wrong?
You have two potential problems here:
async void which means fire and forget policy; better use async Task
You use loop variable i within tasks. Note, that i == 15 after all the tasks
started.
//DONE: async Task instead of async void
private async Task SaveRecordToDB()
{
List<Task> listOfTask = new List<Task>();
for (int i = 0; i <= 15; i++)
{
// Each task should depend on its own local variable
int index = i;
listOfTask.Add(Task.Run(() => IterateRowsSaving(index)));
}
await Task.WhenAll(listOfTask);
}
I am adding messages to a threading channel and reading from this channel constantly. I noticed that if I do not add a Task.Delay then all of the messages are not processed. The program will exit with maybe 10 messages processed when it should be 1000.
Adding a Task.Delay after each write seems hacky. Is there a better way to read all messages in the channel without adding a Task.Delay after each write?
Is there something wrong with my StartListener() method?
internal class Program
{
static List<Task> taskList = new List<Task>();
private static Channel<string> messageList = Channel.CreateUnbounded<string>();
static int midpointCount = 0;
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
sw.Start();
Task.WhenAll(Task.Run(() => StartListener()));
for (int i = 0; i < 10; i++)
{
int task = i;
taskList.Add(Task.Run(() => StartJob(task)));
}
Task.WaitAll(taskList.ToArray());
sw.Stop();
Console.WriteLine("Total Messages Processed: {0} in time {1} MessageListCount {2}", midpointCount, sw.Elapsed, messageList.Reader.Count);
}
private static async Task<string> StartListener()
{
var cancellationtoken = new CancellationTokenSource(TimeSpan.FromMinutes(60)).Token;
await foreach (var msg in messageList.Reader.ReadAllAsync(cancellationtoken))
{
if (!string.IsNullOrEmpty(msg))
{
Console.WriteLine(msg);
Interlocked.Increment(ref midpointCount);
}
}
return "Finished";
}
private static async Task<string> StartJob(int TaskNum)
{
Random rnd = new Random();
for (int i = 0; i < 100; i++)
{
var cancellationtoken = new CancellationTokenSource(TimeSpan.FromMinutes(60)).Token;
try
{
var message = string.Format("TaskNum {0} Message added #{1}", TaskNum, rnd.Next(1, 3000));
await messageList.Writer.WriteAsync(message);
await Task.Delay(50); //<--- Here it seems it will only read all messages with a delay involved.
}
catch (OperationCanceledException)
{
// ignored
}
}
return "Finished";
}
}
Task.WhenAll(Task.Run(() => StartListener()));
StartListener returns a Task. You wrap that in Task.Run, starting another thread to run that task. You then pass than task to the Task.WhenAll method, which returns a Task that you promptly throw away.
The only tasks you add to the taskList variable are the StartJob tasks. Your Main method will finish as soon as all of the StartJob tasks have finished. It will not wait for the StartListener task to finish.
Change your code to wait for the listener to finish.
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
sw.Start();
taskList.Add(Task.Run(() => StartListener()));
for (int i = 0; i < 10; i++)
{
int task = i;
taskList.Add(Task.Run(() => StartJob(task)));
}
Task.WaitAll(taskList.ToArray());
sw.Stop();
Console.WriteLine("Total Messages Processed: {0} in time {1} MessageListCount {2}",
midpointCount, sw.Elapsed, messageList.Reader.Count);
}
This question already has answers here:
Captured variable in a loop in C#
(10 answers)
Closed 2 years ago.
Hi I'm trying to make a simple code to run my function in async way. But the result turn out to be quite unexpected. the result i want is like the counter function can run in parallel way and output the result some way similar like:
Start1
End1
Start2
End2
Start3
Start4
End 3
......
Hi
but it turns out it only get the for loop value i=60 into counter function. I'm quite new to async method and google also cant find the appropriate explanation.
namespace Asycn
{
class Program
{
static async Task Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 60; i++)
{
tasks.Add(Task.Run(()=>counters(i)));
}
await Task.WhenAll(tasks);
Console.WriteLine("Hi");
}
private static void counters(int num)
{
Console.WriteLine("Start"+num.ToString());
Thread.Sleep(num*1000);
Console.WriteLine("End"+num.ToString());
}
}
}
And below is the running result
Running Result
I assume that you are just getting familiar with async here. Generally when you want to process this number of tasks, it's better to limit parallelism with something like plinq, or Parallel.Foreach
The issue is that i is incremented before the Tasks run.
All you need to do is capture the value within the loop:
namespace Asycn
{
class Program
{
static async Task Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 60; i++)
{
var copy = i; // capture state of i
tasks.Add(Task.Run(()=>counters(copy)));
}
await Task.WhenAll(tasks);
Console.WriteLine("Hi");
}
private static void counters(int num)
{
Console.WriteLine("Start"+num.ToString());
Thread.Sleep(num*1000);
Console.WriteLine("End"+num.ToString());
}
}
}
Your code isn't actually using async/await to its fullest potential. You're not capturing the value of i, but you won't have to if you write your code like this:
static async Task Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 60; i++)
{
tasks.Add(counters(i));
}
await Task.WhenAll(tasks);
Console.WriteLine("Hi");
}
private static async Task counters(int num)
{
Console.WriteLine("Start"+num.ToString());
await Task.Delay(num*1000);
Console.WriteLine("End"+num.ToString());
}
The output looks like this:
Start0
End0
Start1
Start2
Start3
...
End1
End2
End3
...
Hi
I want to perform multiple loops in the same time using async Task (I don't want use Parallel.ForEach)
I do :
static async void Run()
{
await MultiTasks();
}
static async Task MultiTasks()
{
var Loop1 = Loop1Async();
var Loop2 = Loop2Async();
await Loop1;
await Loop2;
}
static async Task Loop1Async()
{
for (int i = 0; i < 500; i++)
{
Console.WriteLine("Loop 1 : " + i);
}
}
static async Task Loop2Async()
{
for (int i = 0; i < 500; i++)
{
Console.WriteLine("Loop 2 : " + i);
}
}
Run() is called in my Main method.
But the two loop is not executed in the same time. Once the first lap is completed, the second begins
Why and how to do this ?
You have the fundamental misunderstanding common to beginner users of await.
Let me be very clear about this. Await does not make something asynchronous. It asynchronously waits for an asynchronous operation to complete.
You are awaiting a synchronous operation. There is absolutely nothing asynchronous about the methods you are calling; await does not make them asynchronous. Rather, if they were asynchronous, then the await would return control to the caller immediately, so that the caller could continue to do work. When the asynchronous work finishes, then at some point in the future the remainder of the method is executed, with the awaited result.
Loop1Async and Loop2Async in fact are synchronous. Consider using WriteLineAsync method.
You can also use Task.WhenAll in MultiTasks.
Try .WhenAll(...)
static async Task MultiTasks()
{
var Loop1 = Loop1Async();
var Loop2 = Loop2Async();
await Task.WhenAll(Loop1, Loop2);
}
As others have noted your async methods do not currently yield execution.
You need something that will allow threads to yield back to the system so they can actually run in parallel. Something like this...
static async Task Loop1Async()
{
for (int i = 0; i < 500; i++)
{
Console.WriteLine("Loop 1 : " + i);
await Task.Yield();
}
}
static async Task Loop2Async()
{
for (int i = 0; i < 500; i++)
{
Console.WriteLine("Loop 2 : " + i);
await Task.Yield();
}
}
Your Loop1Async and Loop2Async methods does not have an await inside. For a method to be async it needs to have 1 or more await
I have a windows service (written in C#) that use the task parallel library dll to perform some parallel tasks (5 tasks a time)
After the tasks are executed once I would like to repeat the same tasks on an on going basis (hourly). Call the QueuePeek method
Do I use a timer or a counter like I have setup in the code snippet below?
I am using a counter to set up the tasks, once I reach five I exit the loop, but I also use a .ContinueWith to decrement the counter, so my thought is that the counter value would be below 5 hence the loop would continue. But my ContinueWith seems to be executing on the main thread and the loop then exits.
The call to DecrementCounter using the ContinueWith does not seem to work
FYI : The Importer class is to load some libraries using MEF and do the work
This is my code sample:
private void QueuePeek()
{
var list = SetUpJobs();
while (taskCounter < 5)
{
int j = taskCounter;
Task task = null;
task = new Task(() =>
{
DoLoad(j);
});
taskCounter += 1;
tasks[j] = task;
task.ContinueWith((t) => DecrementTaskCounter());
task.Start();
ds.SetJobStatus(1);
}
if (taskCounter == 0)
Console.WriteLine("Completed all tasks.");
}
private void DoLoad(int i)
{
ILoader loader;
DataService.DataService ds = new DataService.DataService();
Dictionary<int, dynamic> results = ds.AssignRequest(i);
var data = results.Where(x => x.Key == 2).First();
int loaderId = (int)data.Value;
Importer imp = new Importer();
loader = imp.Run(GetLoaderType(loaderId));
LoaderProcessor lp = new LoaderProcessor(loader);
lp.ExecuteLoader();
}
private void DecrementTaskCounter()
{
Console.WriteLine(string.Format("Decrementing task counter with threadId: {0}",Thread.CurrentThread.ManagedThreadId) );
taskCounter--;
}
I see a few issues with your code that can potentially lead to some hard to track-down bugs. First, if using a counter that all of the tasks can potentially be reading and writing to at the same time, try using Interlocked. For example:
Interlocked.Increment(ref _taskCounter); // or Interlocked.Decrement(ref _taskCounter);
If I understand what you're trying to accomplish, I think what you want to do is to use a timer that you re-schedule after each group of tasks is finished.
public class Worker
{
private System.Threading.Timer _timer;
private int _timeUntilNextCall = 3600000;
public void Start()
{
_timer = new Timer(new TimerCallback(QueuePeek), null, 0, Timeout.Infinite);
}
private void QueuePeek(object state)
{
int numberOfTasks = 5;
Task[] tasks = new Task[numberOfTasks];
for(int i = 0; i < numberOfTasks; i++)
{
tasks[i] = new Task(() =>
{
DoLoad();
});
tasks[i].Start();
}
// When all tasks are complete, set to run this method again in x milliseconds
Task.Factory.ContinueWhenAll(tasks, (t) => { _timer.Change(_timeUntilNextCall, Timeout.Infinite); });
}
private void DoLoad() { }
}