For loop async issue C# [duplicate] - c#

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

Related

Why are some of task are not being executed? [duplicate]

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

Load testing with Task.Run() inside a loop vs WhenAll()

I wanted to do some simple load test on a piece of code I wrote with multiple threads hitting a piece of code at once.
Now the below code Sample #1 gave me the expected results. But I am not quite sure why Sample #2 didn't give me the same. Could someone explain, please?
Code sample #1
private static FileCreator _fileCreater;
public static void Main(params string[] args)
{
_fileCreater = new FileCreator();
RunTasks().GetAwaiter().GetResult();
}
private static async Task RunTasks()
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < 100000; i++)
{
tasks.Add(Task.Run(() =>
{
_fileCreater.SaveMessage(new Message());
}));
}
await Task.WhenAll(tasks);
}
Initially, I had the below and I expected the same result, but what I noticed was my CPU never went past 20%, which suggested it wasn't really spanning up multiple threads
Code Sample #2
private static FileCreator _fileCreater;
public static void Main(params string[] args)
{
_fileCreater = new FileCreator();
RunTasks().GetAwaiter().GetResult();
}
private static async Task RunTasks()
{
for (int i = 0; i < 100000; i++)
{
await Task.Run(() =>
{
_fileCreater.SaveMessage(new Message());
});
}
}
Your second piece of code is awaiting each task... so no they will not be run concurrently, as you wait for each to finish before starting the next.

Printing given array in a reverse order utilizing async-await

I'm learning the usage of async and await, and tried to do the following:
I have an array of numbers in a particular order, and an async method that gets a number and a time delay, and return the same passed-in number.
What I'd like to achieve is to print the numbers in a reveresed order (relative to the calling order), utilizing the time delay.
I'm having a hard time figuring out how to do it, and be glad for a guidance. Here's what I have (which, ofcourse, doesn't work):
public static async Task<int> DelayAndReturn(int number, int milisecDelay)
{
await Task.Delay(milisecDelay);
return number;
}
public static void Main(string[] args)
{
int[] arr = { 1, 2, 3 };
int milisecDelay = 10000;
foreach (int num in arr)
{
Console.WriteLine(DelayAndReturn(num, milisecDelay));
milisecDelay /= 10;
}
Console.ReadLine();
}
DelayAndReturn returns a Task object and the correct way to get the result of that object is to await the task. However, awaiting the task will also stop your foreach until 10000 ms have passed, and only then send the next item for processing.
Note that, although the code execution is waiting for the asynchronous operation to complete, the thread is free to be used by other processes.
Your best bet to get them printed in reversed order, is to create a collection of tasks and await all of them.
public static async Task DelayAndReturn(int number, int milisecDelay)
{
await Task.Delay(milisecDelay);
Console.WriteLine(number);
}
public static void Main(string[] args)
{
PrintReversed().GetAwaiter().GetResult();
}
public static async Task PrintReversed() {
int[] arr = { 1, 2, 3 };
int milisecDelay = 1000;
List<Task> tasks = new List<Task>();
foreach (int num in arr)
{
tasks.Add(DelayAndReturn(num, milisecDelay));
milisecDelay /= 10;
}
await Task.WhenAll(tasks);
}

Update Int32 in Task c#

is there any way to increment a value of an int inside a task?
or is this a correct syntax in incrementing an int in task?
sample code:
public int erCount = 9;
static void Main(string[] args){
Task.Factory.StartNew(() => {
...do some task
if(errorfound)
erCount++;
});
Task.Wait();
Console.Writeline(erCount.toString());
}
I seem not to get it in incrementing value inside a thread.
Any help would be great!
Your code is fine as long as you're not modifying the erCount in multiple threads. In which case you'd need a lock or Interlocked.Increment.
Your problem is you're not waiting for the started Task to complete.
public static int erCount = 9;
static void Main(string[] args)
{
var task = Task.Factory.StartNew(() =>
{
...do some task
if(errorfound)
Interlocked.Increment(ref erCount);
});
task.Wait();//Wait for the task to complete
Console.Writeline(erCount.toString());
}
You may altogether remove the shared field and return the error count. That way you can avoid unnecessary synchronization.
public static int erCount = 9;
static void Main(string[] args)
{
var task = Task.Factory.StartNew(() =>
{
int localErrorCount =0;
...do some task
if(errorfound)
localErrorCount++;
return localErrorCount;
});
int errors = task.Result;//Wait for the task to complete and get the error count
erCount += errors;
Console.Writeline(erCount.toString());
}
You could use Interlocked.Increment():
public int erCount = 9;
static void Main(string[] args){
var task = Task.Factory.StartNew(() =>{
...do some task
if(errorfound)
Interlocked.Increment(ref erCount);
});
task.Wait(); // Wait for the task to complete before showing the error count
Console.Writeline(erCount.toString());
}
The reason its not incrementing is:
Console.Writeline(erCount.toString());
executes before the error count has been incremented.
Move that inside the task at the end, and it should work.
You probably need to have a read up on the Task parrallel library and how multithreading works.

Async Task with sync method call

I'm starting with the new .net 4.5 async programming and I found a situation like the code below: I have a sync method but I would like to make several calls and make it run in parallel. However, in this code, all the calls to the sync method runs with the id = 10 and I'm not sure why (probably I misunderstand something with this new approach :).
class Program
{
static void Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
var foo = new Foo();
var fooTask = Task.Run(() => foo.FooNonAsyncMethod(i));
tasks.Add(fooTask);
}
tasks.ForEach(t => t.Wait());
Console.WriteLine("All Done!");
Console.ReadLine();
}
}
public class Foo
{
public void FooNonAsyncMethod(int id)
{
Console.WriteLine("Starting {0}", id);
Thread.Sleep(4000);
Console.WriteLine("Ending {0}", id);
}
}
// Output:
// Starting 10
// Starting 10
// Starting 10
// Starting 10
// Ending 10
// Starting 10
// Starting 10
// Ending 10
// Ending 10
// ...
That's because there is only 1 variable i and the lambda expressions bind on a variable and not a value.
You can fix this by using:
for (int i = 0; i < 10; i++)
{
int newI = i;
var foo = new Foo();
var fooTask = Task.Run(() => foo.FooNonAsyncMethod(newI));
tasks.Add(fooTask);
}
As #Yuriy mentioned, this answer has a lot more info on this particularity : Is there a reason for C#'s reuse of the variable in a foreach?

Categories

Resources