I know async I/O doesn't bring parallelism but I thought when the app is awaiting an async operation it could carry on doing other stuff.
Consider the below code I would expect the loop to carry on while awaiting Wait(i) but obviously I was wrong and each iteration is blocking. What's the correct way to achieve some concurrency here?
using System;
using System.Threading.Tasks;
namespace asynctest
{
class Program
{
static void Main(string[] args)
{
Do().GetAwaiter().GetResult();
}
public static async Task Do()
{
for(int i=0;i<10;i++)
{
await Wait(i);
}
}
public static async Task Wait(int i)
{
await Task.Delay(10000);
}
}
}
public static async Task Do()
{
var tasks = new Task[10];
for (int i = 0; i < 10; i++)
{
tasks[i] = Wait(i);
}
await Task.WhenAll(tasks);
}
You should use WhenAll() to combine the task results, thus your tasks will run in parallel:
namespace asynctest
{
class Program
{
static void Main(string[] args)
{
Do().GetAwaiter().GetResult();
}
public static async Task Do()
{
for(int i=0;i<10;i++)
{
var task1 = Wait(5000);
var task2 = Wait(3000);
int[] result = await Task.WhenAll(task1, task2);
Console.WriteLine("waited for a total of " + result.Sum() + " ms");
}
}
public static async Task<int> Wait(int i)
{
await Task.Delay(i);
return i;
}
}
}
Related
I have the following example code. The problem is, that the Task indicates IsCompleted even though it just await.
longTask only awaits (Task.Delay()) and is not yet finished, so why is IsCompleted true? longTask can't every be completed, because it is caught in while(true)?!
static async Task Main(string[] args)
{
Task longTask = Task.Factory.StartNew(async () =>
{
while (true)
{
long count = 0;
Console.WriteLine("doing hard work.");
while (count < 99999999)
{
count++;
}
Console.WriteLine("wait for a moment");
await Task.Delay(1000);
Console.WriteLine("I have waited enough");
}
});
while (true)
{
Console.WriteLine($"status: {longTask.IsCompleted}");
await Task.Delay(100);
}
}
If you want longTask to represent the inner, never ending, task then call .Unwrap() with appropriate settings or use Task.Run which has the sensible defaults:
public static class Program
{
static async Task Main(string[] args)
{
Task longTask = Task.Factory.StartNew(async () =>
{
while (true)
{
long count = 0;
Console.WriteLine("doing hard work.");
while (count < 99999999)
{
count++;
}
Console.WriteLine("wait for a moment");
await Task.Delay(1000);
Console.WriteLine("I have waited enough");
}
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default).Unwrap();
while (true)
{
Console.WriteLine($"status: {longTask.IsCompleted}");
await Task.Delay(100);
}
}
}
or
public static class Program
{
static async Task Main(string[] args)
{
Task longTask = Task.Run(async () =>
{
while (true)
{
long count = 0;
Console.WriteLine("doing hard work.");
while (count < 99999999)
{
count++;
}
Console.WriteLine("wait for a moment");
await Task.Delay(1000);
Console.WriteLine("I have waited enough");
}
});
while (true)
{
Console.WriteLine($"status: {longTask.IsCompleted}");
await Task.Delay(100);
}
}
}
For more info: MSDN & Startnew is Dangerous
This program does not print the output in the correct order.
public static void Main(string[] args)
{
new Program().Start();
}
public async void Start()
{
int num1 = await GetNumber();
int num2 = await GetNumber();
int num3 = await GetNumber();
Console.WriteLine("Wait...");
Console.ReadKey();
}
public static async Task<int> GetNumber()
{
System.Threading.Thread.Sleep(4000);
Console.WriteLine("Hello");
return 0;
}
It outputs:
--------wait 4Seconds
--------print Hello
--------wait 4Seconds
--------print Hello
--------wait 4Seconds
--------print Hello
--------print wait....
It should output
--------print wait....
--------wait 4Seconds
--------print Hello
--------print Hello
--------print Hello
Use
Await Task.Delay(Timespan.FromMilliSeconds (4000))
instead of Thread.Sleep.
The fully worked out example.
using System;
using System.Threading.Tasks;
namespace Brad
{
public class Program
{
public static void Main(string[] args)
{
var task = new Program().Start();
Console.WriteLine("Wait...");
// You have to put a synchronous Wait() here because
// Main cannot be declared as async
task.Wait();
}
public async Task Start()
{
int num1 = await GetNumber();
int num2 = await GetNumber();
int num3 = await GetNumber();
Console.WriteLine("Finished");
}
public static async Task<int> GetNumber()
{
await Task.Delay(TimeSpan.FromMilliseconds(400));
Console.WriteLine("Hello");
return 0;
}
}
}
You can see it running here
https://dotnetfiddle.net/KHJaDZ
or maybe you wanted the tasks running in parallel instead of one after the other. You can try
using System;
using System.Threading.Tasks;
namespace Brad
{
public class Program
{
public static void Main(string[] args)
{
var task = new Program().Start();
Console.WriteLine("Wait...");
// You have to put a synchronous Wait() here because
// Main cannot be declared as async
task.Wait();
}
public async Task Start()
{
var task1 = GetNumber();
var task2 = GetNumber();
var task3 = GetNumber();
// This runs the tasks in parallel
await Task.WhenAll(task1, task2, task3);
Console.WriteLine("Finished");
}
public static async Task<int> GetNumber()
{
await Task.Delay(TimeSpan.FromMilliseconds(400));
Console.WriteLine("Hello");
return 0;
}
}
}
and this is running here.
https://dotnetfiddle.net/kVk77Z
await means "break the method in half here and come back later when this call finishes". It's how you "convert" a Task<T> into a T: by waiting for (awaiting) the task. Otherwise you are stuck with a Task<T>.
It seems like what you are looking for instead is not awaiting the tasks so that they run asynchronously, but if you did that then you wouldn't be able to get the int results.
(As the other answer mentions, you also need to await something in GetNumber or it will not actually be asynchronous.)
Something like:
public static void Main(string[] args) {
new Program().Start();
}
public void Start() {
GetNumber();
GetNumber();
GetNumber();
Console.WriteLine("Wait...");
Console.ReadKey();
}
public static async Task<int> GetNumber() {
await Task.Delay(TimeSpan.FromSeconds(1));
Console.WriteLine("Hello");
return 0;
}
should give the output you expect:
Wait...
Hello
Hello
Hello
So I've been searching StackOverflow/Google for different methods of running multiple async tasks concurrently. There seemed to be quite the debate between different methods and I just wanted to get some clarification. I'm writing a program to execute a JSON POST request until the server returns a status code of 200. Let's say I want to run 5 of theses tasks in parallel until one returns a status code of 200. Please try not to stray away from the topic, I have no control over the server! Here's my current code,
static bool status = false;
public static async Task getSessionAsync() {
while(!status) { ... }
}
public static async Task doMoreStuff() {
...
}
public static async Task RunAsync()
{
await getSessionAsync ();
await doMoreStuff();
}
public static void Main (string[] args)
{
Task.WhenAll(RunAsync()).GetAwaiter().GetResult();
}
Basically, I'm wondering if it's wrong for me to approach it like this,
public static async Task RunAsync()
{
for(int i = 0; i < 5; i++) {
await getSessionAsync ();
}
await doMoreStuff();
}
This will not run in parallel:
public static async Task RunAsync()
{
for(int i = 0; i < 5; i++) {
await getSessionAsync ();
}
await doMoreStuff();
}
You have to use Task.WhenAny()
public static async Task RunAsync()
{
var tasks = new List<Task>();
for(int i = 0; i < 5; i++) {
tasks.Add(getSessionAsync());
}
await Task.WhenAny(tasks);
await doMoreStuff();
}
If you do not need your current context (i.e. when you are writing a Library and not Frontend code), don't forget to use ConfigureAwait(false) after each await.
Assuming:
private Task<MySession> GetSessionAsync()
{
// ...
}
Option 1
Task.WhenAny
var session = await await Task.WhenAny(Enumerable.Range(0, 5).Select(_ => GetSessionAsync()));
Option 2
You could use the Rx LINQ method called Amb which will observe only the first Observable that returns something.
var session = await Enumerable.Range(0, 5).Select(_ => GetSessionAsync().ToObservable()).Amb().ToTask();
I have this method:
private static async Task MyMethod();
And it is invocated this way:
public static void Main()
{
s_Finishing = false;
Task printTask = PrintStatistics();
MyMethod(serversSawa, serversSterling).Wait();
s_Finishing = true;
}
I expect that PrintStatistics will stop to run only after MyMethod is completed. But unfortunately it doesn`t. If I comment the line s_Finishing = true; The task runs forever - and allows to MyMethod to be completed
How can I solve the issue?
private static async Task PrintStatistics()
{
while (!s_Finishing)
{
long total = 0;
await Task.Delay(TimeSpan.FromSeconds(20));
foreach (var statistic in s_Statistics)
{
ToolsTracer.Trace("{0}:{1}", statistic.Key, statistic.Value);
total += statistic.Value;
}
foreach (var statistic in s_StatisticsRegion)
{
ToolsTracer.Trace("{0}:{1}", statistic.Key, statistic.Value);
}
ToolsTracer.Trace("TOTAL:{0}", total);
ToolsTracer.Trace("TIME:{0}", s_StopWatch.Elapsed);
}
}
private static async Task MyMethod()
{
Parallel.ForEach(
data,
new ParallelOptions { MaxDegreeOfParallelism = 20 }, async serverAndCluster =>
{
await someMethod() });
}
I believe your problem is here:
Parallel.ForEach(..., async ...);
You can't use async with ForEach. It's extremely rare to need to do both parallel (CPU-bound) and async (I/O-bound) together in the same method. If you just want concurrency (which I suspect), use Task.WhenAll instead of ForEach. If you really do need both CPU parallelism and async, then use TPL Dataflow.
I wonder how to accomplish the same thing the below program does without using extra threads or await and async keywords but only Tasks. A sample code would be awesome. It seems to me that we need to use TaskCompletionSource and Async versions of the IO-bound operations or any long-running operations.
static void Main(string[] args)
{
Task t = Go();
Console.WriteLine("Hello World");
Task.Delay(1000).GetAwaiter().OnCompleted(() => { Console.WriteLine("Completed"); });
Console.ReadLine();
}
static async Task Go()
{
var task = PrintAnswerToLife();
await task;
Console.WriteLine("Done");
}
static async Task PrintAnswerToLife()
{
var task = GetAnswerToLife();
int answer = await task;
Console.WriteLine(answer);
}
static async Task<int> GetAnswerToLife()
{
var task = Task.Delay(2000);
await task;
int answer = 21 * 2;
return answer;
}
You can do a pretty straightforward translation of async / await into Task by using ContinueWith. Other translations are also possible, e.g., Task.Delay becomes System.Threading.Timer.
The basic pattern is, for any async method that does an await:
static async Task Go()
{
var task = PrintAnswerToLife();
await task;
Console.WriteLine("Done");
}
becomes:
static Task Go()
{
var tcs = new TaskCompletionSource<object>();
var task = PrintAnswerToLife();
task.ContinueWith(_ =>
{
Console.WriteLine("Done");
tcs.SetResult(null);
});
return tcs.Task;
}
Correct error handling is a lot more work.