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);
}
Related
This question already has answers here:
Thread parameters being changed
(2 answers)
Closed 2 years ago.
I'm creating 10 tasks and every task creates a number with increment. Then i enqueue to ConcurrentQueue As a result i there are 10 numbers in the queue but every numbers value is 10.
How can I set every numbers in queue have different value (0 to 9). Also the TaskTest() method should complete in less than 10 second.
public async void TaskTest()
{
ConcurrentQueue<int> queue;
queue = await GetNumbers();
}
private async Task<ConcurrentQueue<int>> GetNumbers()
{
ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
List<Task> tasks = new List<Task>();
int i = 0;
while (i<10)
{
tasks.Add(Task.Factory.StartNew(() =>
{
var number = CreateNumber(i);
queue.Enqueue(number);
}));
Interlocked.Increment(ref i);
}
foreach (var t in tasks)
{
await t;
}
return queue;
}
private int CreateNumber(int i)
{
Thread.Sleep(1000);
return i;
}
The result:
Regarding while (i<10), if you know the number iterations up front, you should really be using a for loop rather than a while.
Using a for loop or Enumerable.Range you don't need to worry about manipulating i in a thread safe way:
private async Task<ConcurrentQueue<int>> GetNumbers()
{
ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
Task EnqueOnThreadPoolAsync(int i) => Task.Run(() => queue.Enqueue(CreateNumber(i)));
await Task.WhenAll(Enumerable.Range(0, 10).Select(EnqueOnThreadPoolAsync));
return queue;
}
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
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?
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() { }
}