Perform multiple actions in same time - c#

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

Related

Deadlock using async Task and SemaphoreSlim

we are running an ASP.NET 6 webapplication and are having strange issues with deadlocks.
The app suddenly freezes after some weeks of operations and it seems that it might be caused by our locking mechanism with the SemaphoreSlim class.
I tried to reproduce the issue with a simple test-project and found something strange.
The following code is simply starting 1000 tasks where each is doing some work (requesting semaphore-handle, waiting for 10 ms and releasing the semaphore).
I expected this code to simply execute one task after another. But it freezes because of a deadlock in the first call of the DoWork method (at await Task.Delay(10)).
Does anyone know why this causes a deadlock? I tried exactly the same code with ThreadPool.QueueUserWorkItem instead of Task.Run and Thread.Sleep instead of Task.Delay and this worked as expected. But as soon as I use the tasks it stops working.
Here is the complete code-snippet:
internal class Program
{
static int timeoutSec = 60;
static SemaphoreSlim semaphore = new SemaphoreSlim(1);
static int numPerIteration = 1000;
static int iteration = 0;
static int doneCounter = numPerIteration;
static int successCount = 0;
static int failedCount = 0;
static Stopwatch sw = new Stopwatch();
static Random rnd = new Random();
static void Main(string[] args)
{
Task.WaitAll(TestUsingTasks());
}
static async Task TestUsingTasks()
{
while (true)
{
var tasks = new List<Task>();
if (doneCounter >= numPerIteration)
{
doneCounter = 0;
if (iteration >= 1)
{
Log($"+++++ FINISHED TASK ITERATION {iteration} - SUCCESS: {successCount} - FAILURES: {failedCount} - Seconds: {sw.Elapsed.TotalSeconds:F1}", ConsoleColor.Magenta);
}
iteration++;
sw.Restart();
for (int i = 0; i < numPerIteration; i++)
{
// Start indepdent tasks to do some work
Task.Run(async () =>
{
if (await DoWork())
{
successCount++;
}
else
{
failedCount++;
}
doneCounter++;
});
}
}
await Task.Delay(10);
}
}
static async Task<bool> DoWork()
{
if (semaphore.Wait(timeoutSec * 1000)) // Request the semaphore to ensure that one 1 task at a time can enter
{
Log($"Got handle for {iteration} within {sw.Elapsed.TotalSeconds:F1}", ConsoleColor.Green);
var totalSec = sw.Elapsed.TotalSeconds;
await Task.Delay(10); // Wait for 10ms to simulate some work => Deadlock seems to happen here
Log($"RELEASING LOCK handle for {iteration} within {sw.Elapsed.TotalSeconds:F1}. WAIT took " + (sw.Elapsed.TotalSeconds - totalSec) + " seconds", ConsoleColor.Gray);
semaphore.Release();
return true;
}
else
{
Log($"ERROR: TASK handle failed for {iteration} within {sw.Elapsed.TotalSeconds:F1} sec", ConsoleColor.Red);
return false;
}
}
static void Log(string message, ConsoleColor color)
{
Console.ForegroundColor = color;
Console.WriteLine(message);
Console.ForegroundColor = ConsoleColor.White;
}
}
Thanks in advance!
But it freezes because of a deadlock in the first call of the DoWork method (at await Task.Delay(10)).
I would argue that it is not deadlock but a thread starvation issue. If you wait long enough you will see that threads will be able to finish the simulation wait from time to time.
The quick fix here is using non-blocking WaitAsync call with await:
static async Task<bool> DoWork()
{
if (await semaphore.WaitAsync(timeoutSec * 1000))
{
...
}
}
Also note:
It is recommended to wrap the code after Wait.. into try-finally block and release the semaphore in the finally.
Incrementing counters in parallel environments better should be done in atomic fashion, for example with Interlocked.Increment.

Task.WhenAll adding a delay between requests (httpclient) [duplicate]

I have a loop that creates 5 Tasks. How can I insert a Delay of 5 seconds between each Task. I don't know how to fit Task.Delay(5000) in there.
var tasks = new List<Task<int>>();
for (var i = 0; i < 5; i++)
{
tasks.Add(ProcessQueueAsync());
}
await Task.WhenAll(tasks);
My ProcessQueAsync method calls a server, retrieves data and returns and int.
private async Task<int> ProcessQueAsync()
{
var result = await CallToServer();
return result.Count;
}
for (var i = 0; i < 5; i++)
{
tasks.Add(ProcessQueueAsync());
await Task.Delay(5000);
}
Or:
for (var i = 0; i < 5; i++)
{
await ProcessQueueAsync();
await Task.Delay(5000);
}
Depending on that you want.
If you want the tasks to run one after the other, with a 5 second delay, you should perhaps look at Task.ContinueWith instead of using Task.WhenAll. This would allow you to run tasks in serial rather than in parallel.

How to break out of an IAsyncEnumerable when iterating?

C# 8 adds support for asynchronuous iterator blocks, so you can await things and return an IAsyncEnumarator instead of an IEnumerable:
public async IAsyncEnumerable<int> EnumerateAsync() {
for (int i = 0; i < 10; i++) {
yield return i;
await Task.Delay(1000);
}
}
With a non-blocking consuming code that looks like this:
await foreach (var item in EnumerateAsync()) {
Console.WriteLine(item);
}
This will result in my code running for about 10 seconds. However, sometimes I want to break out of the await foreach before all elements are consumed. With an breakhowever, we would need to wait until the current awaited Task.Delay has finished. How can we break immediately out of that loop without waiting for any dangling async tasks?
The use of a CancellationToken is the solution since that is the only thing that can cancel the Task.Delay in your code. The way we get it inside your IAsyncEnumerable is to pass it as a parameter when creating it, so let's do that:
public async IAsyncEnumerable<int> EnumerateAsync(CancellationToken cancellationToken = default) {
for (int i = 0; i < 10; i++) {
yield return i;
await Task.Delay(1000, cancellationToken);
}
}
With the consuming side of:
// In this example the cancellation token will be caneled after 2.5 seconds
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2.5));
await foreach (var item in EnumerateAsync(cts.Token)) {
Console.WriteLine(item);
}
Sure, this will cancel the enumeration after 3 elements were returned, but will end in an TaskCanceledException thrown out of Task.Delay. To gracefully exit the await foreach we have to catch it and break on the producing side:
public async IAsyncEnumerable<int> EnumerateAsync(CancellationToken cancellationToken = default) {
for (int i = 0; i < 10; i++) {
yield return i;
try {
await Task.Delay(1000, cancellationToken);
} catch (TaskCanceledException) {
yield break;
}
}
}
Note
As of now this is still in preview and is subject to possible change. If you are interested in this topic you can watch a discussion of the C# language team about CancellationToken in an IAsyncEnumeration.

Prevent winforms UI block when using async/await

I'm fairly new to async/await programming and sometimes I feel that I understand it, and then all of a sudden something happens and throws me for a loop.
I'm trying this out in a test winforms app and here is one version of a snippet that I have. Doing it this way will block the UI
private async void button1_Click(object sender, EventArgs e)
{
int d = await DoStuffAsync(c);
Console.WriteLine(d);
}
private async Task<int> DoStuffAsync(CancellationTokenSource c)
{
int ret = 0;
// I wanted to simulator a long running process this way
// instead of doing Task.Delay
for (int i = 0; i < 500000000; i++)
{
ret += i;
if (i % 100000 == 0)
Console.WriteLine(i);
if (c.IsCancellationRequested)
{
return ret;
}
}
return ret;
}
Now, when I make a slight change by wrapping the body of "DoStuffAsync()" in a Task.Run it works perfectly fine
private async Task<int> DoStuffAsync(CancellationTokenSource c)
{
var t = await Task.Run<int>(() =>
{
int ret = 0;
for (int i = 0; i < 500000000; i++)
{
ret += i;
if (i % 100000 == 0)
Console.WriteLine(i);
if (c.IsCancellationRequested)
{
return ret;
}
}
return ret;
});
return t;
}
With all that said, what is the proper way to handle this scenario?
When you write such code:
private async Task<int> DoStuffAsync()
{
return 0;
}
This way you are doing things synchronously, because you are not using await expression.
Pay attention to the warning:
This async method lacks 'await' operators and will run synchronously.
Consider using the 'await' operator to await non-blocking API calls,
or 'await Task.Run(...)' to do CPU-bound work on a background thread.
Based on the warning suggestion you can correct it this way:
private async Task<int> DoStuffAsync()
{
return await Task.Run<int>(() =>
{
return 0;
});
}
To learn more about async/await you can take a look at:
Async and Await by Stephen Cleary
Asynchronous Programming with Async and Await from msdn
You just need to change the DoStuffAsync task little bit, as below.
private async Task<int> DoStuffAsync(CancellationTokenSource c)
{
return Task<int>.Run(()=> {
int ret = 0;
// I wanted to simulator a long running process this way
// instead of doing Task.Delay
for (int i = 0; i < 500000000; i++)
{
ret += i;
if (i % 100000 == 0)
Console.WriteLine(i);
if (c.IsCancellationRequested)
{
return ret;
}
}
return ret;
});
}

Why do async await without being told

I am trying to make part of my system run in parallel, but some some reason do it wait for each element before it starts the next even though i have not told it to await. I would like to start executing ExecuteItem for each this.Items and then continue when they are all done.
bool singleThread = false;
public async Task Execute()
{
if (!this.singleThread)
{
var tasks = this.Items.Select(x => this.ExecuteItem(x)).ToArray();
Task.WaitAll(tasks);
}
else
{
foreach (var item in this.Items)
{
await this.ExecuteItem(item);
}
}
}
private async Task ExecuteItem(IMyItem item)
{
MappedDiagnosticsContext.Set("itemRef", item.ItemRef);
try
{
await this.HandelItem(item);
}
catch (Exception exp)
{
Logger.ErrorException(string.Format("Execution for {0} failed.", item.ItemName), exp);
Logger.Error("Error Message: ", exp.Message);
}
MappedDiagnosticsContext.Remove("itemRef");
}
To make clear my problem my code behaves as if had wrote the following
var tasks = this.Items.Select(x => await this.ExecuteItem(x)).ToArray();
To make sure it was not some kind of linq problem have i rewriten the problem code to the following, however the code still blocks tasks[i] = this.ExecuteItem(this.Items[i]);
Task[] tasks = new Task[this.Items.Count];
for (int i = 0; i < this.Items.Count; i++)
{
Console.WriteLine("Adding " + this.Items[i].ItemName);
tasks[i] = this.ExecuteItem(this.Items[i]);
}
Console.WriteLine("Waiting!!!");
Task.WaitAll(tasks);
Something in HandelItem is blocking.
async methods don't run completely asynchronously, they execute synchronously up until the point they hit an await. So all of ExecuteItem, up to HandelItem will run before the tasks list is built. This synchronous behavior would continue into HandelItem if it is an async method, so likely HandelItem is executing while building up the tasks list.
This is easily seen with this example program:
static void Main(string[] args)
{
var items = Enumerable.Range(1, 2);
Console.WriteLine("Start");
var tasks = items.Select(i => AsyncMethod(i)).ToArray();
Console.WriteLine("Got tasks");
Task.WaitAll(tasks);
Console.WriteLine("Done!");
}
static async Task AsyncMethod(int i)
{
Console.WriteLine("Enter {0}", i);
await AsyncMethod2(i);
await Task.Delay(1000);
Console.WriteLine("Exit {0}", i);
}
static async Task AsyncMethod2(int i)
{
Console.WriteLine("Enter2 {0}", i);
await Task.Delay(2000);
Console.WriteLine("Exit2 {0}", i);
}
It's output is:
Start
Enter 1
Enter2 1
Enter 2
Enter2 2
Got tasks
Exit2 2
Exit2 1
Exit 1
Exit 2
Done!
So both async methods run while building the task list, up until the point that they have to wait. So if HandelItem does something non-asynchronous, it will cause blocking.
If you want the tasks to execute in parallel; and wait until all are complete:
await Task.WhenAll(this.Items.Select(item=>this.ExecuteItem(item)));

Categories

Resources