In C#,
static async Task<int> Task1()
{
await Task.Delay(1000); // delaying 1 sec
return 10;
}
static async Task<int> Task2()
{
await Task.Delay(2000); // delaying 2 sec
return 10;
}
static async Task<int> Task3()
{
await Task.Delay(3000); // delaying 3 sec
return 10;
}
when performing below operations it takes 6 sec
int a = await Task1();
int b = await Task2();
int c = await Task3();
return a+b+c;
but when performing below operations it takes only 3 sec
var a = Task1();
var b = Task2();
var c = Task3();
return await a + await b + await c;
results are getting as 30 from both the snippet but duration differs...
Can anyone please clarify it
reference - https://dotnetfiddle.net/I31eqK
When you do this:
int a = await Task1();
int b = await Task2();
int c = await Task3();
return a+b+c;
each task is awaited before the next one is started. That is, each task must have returned a result before the next task is awaited. Thus, the total time is the total time for all tasks.
But when you do this:
var a = Task1();
var b = Task2();
var c = Task3();
return await a + await b + await c;
the three tasks are started in parallel, and THEN you await their results. Thus in this case the total time is whatever the longest task takes.
The code below works synchronously. Meaning that you are queuing up the tasks in series (therefore executing them one-by-one) as the functions wait patiently for the ones above them to finish like gentlemen.
Figuratively: "waiting for one to finish their sentence before speaking".
int a = await Task1(); // wait for the above function/Task to complete THEN run this one ("wait for one to finish their sentence THEN talk")
int b = await Task2(); // wait for the above function/Task to complete THEN run this one ("wait for one to finish their sentence THEN talk")
int c = await Task3(); // wait for the above function/Task to complete THEN run this one ("wait for one to finish their sentence THEN talk")
return a+b+c; // return the values received
In the code below, Instead of waiting the function to finish and return one-by-one, you are instead ("asynchronously") waiting for all of the included variables to have a value (as you are not waiting for one function to finish before running the next one, but rather waiting for every necessary value to exist).
Figuratively: "Talking over each other, therefore ending the conversation earlier than waiting for one to finish their sentence".
var a = Task1(); // Run this function while other functions are running ("Talk while other people are talking")
var b = Task2(); // Run this function while other functions are running ("Talk while other people are talking")
var c = Task3(); // Run this function while other functions are running ("Talk while other people are talking")
return await a + await b + await c; // now wait for every variable to have their own value and THEN return
Examples
Here's an example to make this easy to understand:
int a = Task1(); // Asynchronous call ("Dont wait for someone to finish speaking")
int b = Task2(); // Asynchronous call ("Dont wait for someone to finish speaking")
int c = await Task3(); // Synchronous call ("wait for someone/people to finish talking)
The code above should take 5 seconds because:
Task1() pauses for 1000 milliseconds and runs at the same time as Task2() with its 2000 milliseconds (as Task1 and Task2 are not waiting for each other to finish), As Task1 Finishes in 1 second where as Task2 will finish in 2 Seconds, therefore taking it 2 seconds before Task3 is called
Task3(), with its 3000 millisecond pause, will wait patiently until all functions before it are done with their own thing. Because Task2 is finished, Task3 is then called, adding an additional 3 seconds to the 2 seconds Total.
Extra Note: Please run this script (not on dotnetfiddle.com as it doesn't show the live output but rather on your local machine): https://www.toptal.com/developers/hastebin/igapunubis.csharp
With
var a = Task1(); // Started here
var b = Task2(); // Started here
var c = Task3(); // Started here
var r = await a + await b + await c;
the tasks are started pretty much at the same time.
With:
int a = await Task1();
int b = await Task2();
int c = await Task3();
return a+b+c;
the 2nd task starts only when the first one finishes.
The following code would take ~6 seconds:
Func<Task<int>> a = async () => await Task1(); // Not started here
var b = async () => await Task2(); // Not started here
var c = async () => await Task3(); // Not started here
var r = await a() + await b() + await c();
Console.WriteLine(r);
When you do the following:
int a = await Task1();
int b = await Task2();
int c = await Task3();
You are awaiting the task to complete, in other words, once execution of Task1 starts, it will not execute Task2 untill Task1 completes. You are awaiting the Task to complete.
When you do:
var a = Task1();
var b = Task2();
var c = Task3();
You are not awaiting for each individual task to complete, instead, you are executing the Tasks, and the thread pool manages these tasks, and in some cases, can run them simultaniously, resulting in faster times. For example, if Task1, Task2, Task3 are run in parralel, best case scenario, is that it will take 3 seconds to complete.
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
I'm looking for a simple C# example of a pattern that meets the following requirements:
I want to execute 2 methods asynchronously, initiating the second without waiting for the first to finish.
Each method accepts a parameter and returns and output value.
I then want to wait for the results of both before moving on.
This seemed like a scenario where I could easily use the async and await keywords but after looking at a couple of Hello World examples, I was surprised that I could not easily meet all of the above criteria at the same time. For example, while using the await keyword kept my UI from being non blocking and allowing my app to continue to be responsive to events while I await the completion of a method, the methods were still executing synchronously. Or if I was able to execute asynchronously, I had trouble trying to pass an input parameter to the calling method and receiving a result back.
The following seems to meet my requirements and leads me to believe that I'd be able to make great use out of it but first I'd like to get your feedback on how it can be improved.
I've played around with Threads before and found that make my code much more complex and was expecting that the "async and await" keywords would make my life easier but the following code which appears to meet my requirements does not even use these keywords. Am I using the "Parallel Task Library" here? If so, how would you contrast the functionality in this library to the async and await functionality?
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ASyncCourse
{
static class Program
{
static void Main()
{
int result1 = 0;
int result2 = 0;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
Task task1 = Task.Run(() => result1 = DoSomeWork(1));
Debug.WriteLine($"After Task1: {stopWatch.Elapsed}");
Task task2 = Task.Run(() => result2 = DoSomeOtherWork(10));
Debug.WriteLine($"After Task2: {stopWatch.Elapsed}");
Task.WaitAll(task1, task2);
stopWatch.Stop();
int sum;
sum = result1 + result2;
Debug.WriteLine($"Sum: {sum}");
Debug.WriteLine($"Final: {stopWatch.Elapsed}");
}
private static int DoSomeOtherWork(int waitFor)
{
Thread.Sleep(waitFor * 1000);
return waitFor;
}
private static int DoSomeWork(int waitFor)
{
Thread.Sleep(waitFor * 1000);
return waitFor;
}
}
}
You can use the async and await pattern and await the cpu bound tasks, however you will need to use async Task Main() entry point
public static async Task Main()
{
var task1 = Task.Run(() => CpuWork1(1));
var task2 = Task.Run(() => CpuWork2(10));
var result1 = await task1;
var result2 = await task2;
// or
var results = await Task.WhenAll(task1, task2) ;
}
If your workloads are IO bound, then they would have the async Task<T> signature, and you would just await the methods returned tasks, similar to above (and not use Task.Run)
Complete Example
static async Task Main()
{
var stopWatch = new Stopwatch();
stopWatch.Start();
var task1 = Task.Run(() => DoSomeWork(1));
var task2 = Task.Run(() => DoSomeOtherWork(10));
var results = await Task.WhenAll(task1, task2);
stopWatch.Stop();
Debug.WriteLine($"Sum: {results.Sum()}");
Debug.WriteLine($"Final: {stopWatch.Elapsed}");
}
Or similarly with an async methods
static async Task Main()
{
var stopWatch = new Stopwatch();
stopWatch.Start();
var task1 = DoSomeWorkAsync(1);
var task2 = DoSomeOtherWorkAsync(10);
var results = await Task.WhenAll(task1, task2);
stopWatch.Stop();
Debug.WriteLine($"Sum: {results.Sum()}");
Debug.WriteLine($"Final: {stopWatch.Elapsed}");
}
private static async Task<int> DoSomeOtherWorkAsync(int waitFor)
{
// some IO bound workload
await Task.Delay(waitFor * 1000);
return waitFor;
}
private static async Task<int> DoSomeWorkAsync(int waitFor)
{
// some IO bound workload
await Task.Delay(waitFor * 1000);
return waitFor;
}
Asynchronous methods are method designed to access external resources without blocking and without wasting resources(extra threads)
Use Task.Delay(1000) to simulate asynchronous method.
private async Task<int> LoadSomething(int value)
{
await Task.Delay(value * 1000);
return value;
}
Then
var task1 = LoadSomething(2);
// executes next line without waiting for previous line to complete
var task2 = LoadSomething(5);
await Task.WhenAll(task1, task2); // takes only 5 seconds
var result1 = task1.Result;
var result2 = task2.Result;
you use await keyword. async operations perform parallelly. when we need to wait until we get the result use await before it.
Example:
var task1 = AsyncOperation1(2);
var task2 = AsyncOperation1(5);
var result1 = await task1;
var result2 = await task2;
use last two line instead of Task.WhenAll(task1, task2);
Use async on all of your methods and have them return Task.
Call await Task.Delay instead of Thread.Sleep
Then, you can use await Task.WhenAll to wait for your tasks to complete.
If you do not put await in front of your method calls, execution proceeds without waiting for a result.
You can call Result to get the result once you have awaited them.
static async Task Main()
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
Task<int> task1 = DoSomeWork(1);
Console.WriteLine($"After Task1: {stopWatch.Elapsed}");
Task<int> task2 = DoSomeOtherWork(10);
Console.WriteLine($"After Task2: {stopWatch.Elapsed}");
await Task.WhenAll(task1, task2);
stopWatch.Stop();
int sum = task1.Result + task2.Result;
Console.WriteLine($"Sum: {sum}");
Console.WriteLine($"Final: {stopWatch.Elapsed}");
}
private static async Task<int> DoSomeOtherWork(int waitFor)
{
await Task.Delay(waitFor * 1000);
return waitFor;
}
private static async Task<int> DoSomeWork(int waitFor)
{
await Task.Delay(waitFor * 1000);
return waitFor;
}
output
After Task1: 00:00:00.0037888
After Task2: 00:00:00.0094963
Sum: 11
Final: 00:00:10.0126715
I have the following code:
//Await #1
var response1 = await doSomething();
if(response1.isSuccess) {
//Await #2
var response2 = await doSomethingElse();
}
Response 1 and response 2 are totally independent and i want to parallelize the await task here.
Basically response 2 takes a lot of time and hence is only invoked when response1 is success.
Is there any way in which i can invoke both tasks and see the result of response 1 and if it is fail, i drop/skip the response of Await#2.
Essentially what you want is to cancel a task, but with a little more logic.
You need to edit doSomethingElse so that it accepts a CancellationToken, and also so that it makes use of it to stop what its doing:
public async Task<Foo> DoSomethingElse(CancellationToken token) {
...
if (token.IsCancellationRequested) {
// stop what you are doing...
// I can't tell you how to implement this without seeing how DoSomethingElse is implemented
}
...
}
Now, get a CancellationToken from a CancellationTokenSource:
var source = new CancellationTokenSource();
var token = source.Token;
And here comes the logic of "if response 1 fails cancel response 2":
var response2Task = DoSomethingElse(token);
var response1 = await DoSomething();
if (!response1.IsSuccess) {
source.Cancel();
} else {
var response2 = await response2Task;
}
var task2 = doSomethingElse();
var response1 = await doSomething();
if(response1.isSuccess) {
var response2 = await task2;
}
This will start the execution of doSomethingElse() immediately, and only wait for its completion when response1.isSuccess == true
You can launch both threads, then once the first task obtains a result, you could either stop, or 'wait' for the second thread to finish.
The conditional logic, imo, should be placed in the first task's thread. If you do not have access to the doSomething, make a lambda in which you will await doSomething's response and then proceed with the condition logic.
How to stop a thread? Here you go, and here you go
This is what you can do:
var task1 = Task.Run(() =>
{
Console.WriteLine("running task 1");
return 1;
});
var task2 = Task.Run(() =>
{
Console.WriteLine("running task 2");
return 2;
});
await Task.WhenAll(task1, task2);
So you will run the 2 tasks in parallel. If you need to check the result of particular tasks, this is the way to do that(it won't trigger the task again if you run Task.WhenAll() previously. It will just get you the results of previously executed tasks):
var result1 = await task1;
var result2 = await task2;
Then you can apply some condition:
if(result1 == 1) return result2;
etc..
I have the two APIs, which call the same services.
This version performs in the time that the at most expensive task takes.
public async Task<double> Do()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var t1 = _service.Do1();
var t2 = _service.Do2();
await t1;
await t2;
return sw.Elapsed.TotalMilliseconds;
}
While this one performs in sum of each task delay.
public async Task<double> Do()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
await _service.Do1();
await _service.Do2();
return sw.Elapsed.TotalMilliseconds;
}
internal async Task Do1() => await Task.Delay(5000);
internal async Task Do2() => await Task.Delay(2000);
Why is that, what is actually happening ?
Tasks return "hot", or already started. The await keyword quite literally means hold here and wait for the task to complete. As such, when you await each operation individually, it's calling Do1, waiting for that to finish, and then calling Do2 and waiting for that to finish.
However, when you just store them in variables, it calls Do1, and then it calls Do2, while Do1 is still running. Later when you await these two variables, the code will hold waiting for each to actually complete on by one, but they're already both running.
Long and short, it's the difference between running serially and in parallel. Asynchronous operations can be run parallel, but they are not parallel by nature: it's two different concepts.
var t1 = _service.Do1(); < --- your 1st task started
var t2 = _service.Do2(); < --- your 2nd task started
await t1; < --- 1st awaited but it already did at least part of its job so far
await t2; < --- 2nd task awaited but, if its light task, probably already done so nothing to await
vs
await _service.Do1(); < --- 1st task started and blocks 2nd from starting
await _service.Do2(); < --- 2nd starts only after 1st finished
Obviously, case 1 runs in parallel for some time while second one runs in series thus takes full time of both tasks summed.
In the first case both tasks will be started before you await one of them. So you need only the time max(time(task1), time(task2)).
In the second case you start the first task wait for it to finish and then start the second one, so your time will be time(task1) + time(task2)
Edit:
Also be aware of CPU-bound Tasks as Tasks will not automatically run parallel if executed on the same thread, e.g.
public static async Task<double> Do()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var t1 = Do1();
var t2 = Do2();
await t1;
await t2;
var time = sw.Elapsed.TotalMilliseconds;
Console.WriteLine("time="+time); // time=3002.3204
return time;
}
public static async Task Do1()
{
var t = Task.Delay(1000);
while(t.Status != TaskStatus.RanToCompletion) {}
await t;
}
public static async Task Do2()
{
var t = Task.Delay(2000);
while(t.Status != TaskStatus.RanToCompletion) {}
await t;
}
Try it online!
I have several similar tasks, each of which is limited to a timeout, i.e. must be completed faster than the specified time or return an empty result. The main purpose of these tasks is receiving a response from the server with a timeout limit.
An example of such task is below:
public async Task<List<Data>> GetDataWithTimeoutAsync(InputData data, int timeout)
{
List<Data> resultData = new List<Data>;
await Task.WhenAny(Task.Run(async () =>
{
resultData.Add(SomeWork(data));
}),
Task.Delay(timeout));
return resultData;
}
Each of these tasks works correctly separately.
But I want to run some of such tasks in parallel. For this, I use the following code.
public async Task<List<List<Data>>> GetAllDataAsync()
{
var resultTasks = new ConcurrentBag<Task<List<Data>>>();
var firtsTask = GetDataWithTimeoutAsync(firstInputData, firtsTimeout);
var secondTask = GetDataWithTimeoutAsync(secondInputData, secondTimeout);
var thirdTask = GetDataWithTimeoutAsync(thirdInputData, thirdTimeout);
resultTasks.Add(Task.Run(() => firtsTask));
resultTasks.Add(Task.Run(() => secondTask));
resultTasks.Add(Task.Run(() => thirdTask));
await Task.WhenAll(resultTasks);
var result = resultTasks.Select(t => t.Result).ToList();
return result;
}
But this code works incorrectly if different timeouts are set for nested tasks. In this case all of tasks are completed after the smallest of the timeouts.
How i can run some tasks in parallel with WhenAll if each of task is a result of WhenAny?
Your code doesn't compile, so I wrote something similar. I can't reproduce your results. In my case the WhenAll with different timeouts works as expected. It completes when the longest running task is completed, which is the second one (200 msec).
public static async Task Main(string[] args)
{
var task1 = GetDataAsync(100).WithTimeout(50); // Should timeout after 50 msec
var task2 = GetDataAsync(200).WithTimeout(300); // Should complete after 200 msec
var task3 = GetDataAsync(300).WithTimeout(100); // Should timeout after 100 msec
var stopwatch = Stopwatch.StartNew();
var results = await Task.WhenAll(task1, task2, task3); // Wait for all
stopwatch.Stop();
Console.WriteLine($"Results: {String.Join(", ", results)}");
Console.WriteLine($"Elapsed: {stopwatch.ElapsedMilliseconds} msec");
}
private static async Task<int> GetDataAsync(int input) // the input is used as delay
{
await Task.Delay(input);
return input;
}
public static Task<T> WithTimeout<T>(this Task<T> task, int timeout)
{
var delayTask = Task.Delay(timeout).ContinueWith(_ => default(T),
TaskContinuationOptions.ExecuteSynchronously);
return Task.WhenAny(task, delayTask).Unwrap();
}
Output:
Results: 0, 200, 0
Elapsed: 211 msec
Let's say I have two async methods
public async static Task RunAsync1()
{
await Task.Delay(2000);
await Task.Delay(2000);
}
and
public async static Task RunAsync2()
{
var t1 = Task.Delay(2000);
var t2 = Task.Delay(2000);
await t1;
await t2;
}
Then I use it like
public static void M()
{
RunAsync1().GetAwaiter().GetResult();
RunAsync2().GetAwaiter().GetResult();
}
In a result the RunAsync1 will run 4sec but RunAsync2 only 2sec
Can anybody explain why? Methods are almost the same. What is the difference?
In the second method 2 tasks are started at the same time. They will both finish in 2 seconds (as they are running in parallel). In the first method you first run one method (2 seconds), wait for it to complete, then start the second one (2 more seconds). The key point here is Task.Delay(..) starts right when you call it, not when you await it.
To clarify more, first method:
var t1 = Task.Delay(2000); // this task is running now
await t1; // returns 2 seconds later
var t2 = Task.Delay(2000); // this task is running now
await t2; // returns 2 more seconds later
Second method:
var t1 = Task.Delay(2000);
var t2 = Task.Delay(2000); // both are running now
await t1; // returns in about 2 seconds
await t2; // returns almost immediately, because t2 is already running for 2 seconds
Just examine your code:
public async static Task RunAsync1()
{
await Task.Delay(2000); // Start a delay task, and WAIT for it to finish
await Task.Delay(2000); // Start a delay task, and WAIT for it to finish
}
So the second await Task.Delay(2000); is called after the first call is finished (after 2 seconds).
While the second method,
public async static Task RunAsync2()
{
var t1 = Task.Delay(2000); // Start a task
var t2 = Task.Delay(2000); // Start a task
await t1; // Wait for task to finish
await t2; // Wait for task to finish
}
So tasks t1, and t2 run at the same time.
If you change it to
public async static Task RunAsync3()
{
var t1 = Task.Delay(2000); // Start a task
await t1; // Wait for task to finish
var t2 = Task.Delay(2000); // Start a task
await t2; // Wait for task to finish
}
you would get the same results as in RunAsync1.
In the first case you are saying
public async static Task RunAsync1()
{
var t1 = Task.Delay(2000);
await t1;
var t2 = await Task.Delay(2000);
await t2;
}
Which equates to
0:00 Create a callback in 2 seconds 0:00
0:00 Wait until the callback has returned 0:02
0:02 Create a callback in 2 seconds 0:02
0:02 Wait until the callback has returned 0:04
0:04 return;
The second case is
public async static Task RunAsync2()
{
var t1 = Task.Delay(2000);
var t2 = Task.Delay(2000);
await t1;
await t2;
}
0:00 Create callbacks in 2 seconds 0:00
0:00 Create callbacks in 2 seconds 0:00
0:00 Wait for first callback 0:02
0:02 Wait for the second callback 0:02
0:02 return
In other words, in the first one you are doing sequential asynchronous programming, and the second is parallel asynchronous programming.
Whenever you start a Task. It already started when you created it, not when you called await.
If you create a task and put it in a variable, it might already finish when you await that. This is what happen to your second case. await just ensures that it must finish before continuing.