Why is my method not running asynchronously? - c#

I'm running a test that should be calling a method multiple times without waiting for its result. Here's my code:
private async Task HandleJob(string params) {
// do stuff that takes a minute
Thread.Sleep(10000);
return;
}
[TestMethod]
public async Task StartTest() {
HandleJob("one");
HandleJob("two");
}
When I set a break at HandleJob("two"), it only gets hit after one has been completed. How do I make them run asynchronously, so no waiting is done?

You haven't used await in the method at all. This means that the method will execute the lines synchronously. To actually yield back to the caller and let the remaining code run asynchronously, you need to use await, which will continue the method as a continuation on the targeted Task (as well as unboxing any exceptions and return values)
There are a couple of helper methods in Task to assist with this - my favorite is Task.Yield(), which will immediately return, thus an await Task.Yield() will spit out into a new thread and return all at once.
[TestMethod]
public async Task StartTest() {
await Task.Yield(); // await yields control, and Yield will ensure the rest of the method starts immediately
HandleJob("one");
HandleJob("two");
}
If you want the individual subtasks to execute all at once, conglomerate them using Task.WhenAll and Task.Run
[TestMethod]
public async Task StartTest() {
await Task.WhenAll(
Task.Run(() => HandleJob("one")),
Task.Run(() => HandleJob("two")));
}

Your asynchronous method HandleJob is not really async as Thread.Sleep blocks the current thread. Try using await Task.Delay instead. Note that params is a keyword and your original code won't compile. I've changed it to parameters.
private async Task HandleJob(string parameters) {
// do stuff that takes a minute
await Task.Delay(10000);
return;
}
In your start method you can return a single Task that gets completed when both methods are finished using Task.WhenAll as pointed out in one of the other answers. If you return the result rather than await it, then the caller of StartTest can determine whether he waits for the Task to complete or if he ignores it and it becomes a fire and forget situation. Because you are not using await StartTest will not need to be marked as async anymore.
[TestMethod]
public Task StartTest() {
return Task.WhenAll(HandleJob("one"), HandleJob("two"));
}

you need to await both the tasks:
private void HandleJob( params string[] value )
{
return;
}
[TestMethod]
public async Task StartTest()
{
var task1 = Task.Run( () => HandleJob( "one" ) );
var task2 = Task.Run( () => HandleJob( "two" ) );
await Task.WhenAll( task1 , task2 );
}

Related

C# TaskWhenAll on method which is returing result depending on awaited call

i'm trying to make this code works in async way, but i got some doubts.
public async Task<string> GetAsync(string inputA, string inputB)
{
var result = await AnotherGetAsync(inputA, inputB)
.ConfigureAwait(false);
return result.Enabled
? inputB
: string.Empty;
}
I got some string input collection, and i would like to run this method on them.
Then i would do Task.WhenAll, and filter for non empty strings.
But it won't be async as inside the method i'm already awaiting for the result right?
I assumed the real question is:
If a single item is awaited inside method A, will this run sequential if I use Task.WhenAll for a range of items calling method A?
They will be run simultaneous with Task.WhenAll.
Perhaps it is best explained with an example:
void Main()
{
Test().GetAwaiter().GetResult();
}
private async Task<bool> CheckEmpty(string input)
{
await Task.Delay(200);
return String.IsNullOrEmpty(input);
}
private async Task Test()
{
var list = new List<string>
{
"1",
null,
"2",
""
};
var stopwatch = new Stopwatch();
stopwatch.Start();
// This takes aprox 4 * 200ms == 800ms to complete.
foreach (var itm in list)
{
Console.WriteLine(await CheckEmpty(itm));
}
Console.WriteLine(stopwatch.Elapsed);
Console.WriteLine();
stopwatch.Reset();
stopwatch.Start();
// This takes aprox 200ms to complete.
var tasks = list.Select(itm => CheckEmpty(itm));
var results = await Task.WhenAll(tasks); // Runs all at once.
Console.WriteLine(String.Join(Environment.NewLine, results));
Console.WriteLine(stopwatch.Elapsed);
}
My test results:
False
True
False
True
00:00:00.8006182
False
True
False
True
00:00:00.2017568
If we break this down:
AnotherGetAsync(inputA, inputB)
Returns a Task. Using the await keyword implicitly returns another Task to the code calling GetAsync(string inputA, string inputB).
Using the await keyword will free up the current thread, which is what makes the code asynchronous.
once the Task returned by AnotherGetAsync(inputA, inputB) is complete, result will be set to that Task.Result.
The remainder of the method will then resume:
return result.Enabled
? inputB
: string.Empty;
Setting the Task.Result of the Task returned from GetAsync.
If GetAsync is to be run multiple times, you can use Task.WhenAll to wait for each resulting Task to complete:
Task.WhenAll(yourStringCollection.Select(s => GetAsync(s, "another string"));
Task.WhenAll itself returns another Task that will complete when all the GetAsync tasks have completed:
var strings = await Task.WhenAll(yourStringCollection.Select(s => GetAsync(s, "another string"));
var nonEmptyStrings = strings.Where(s => s != string.Empty);
No, it will be async. When you await a task inside an async method, the task will be awaited when the async method is invoked. Here is your example simplified, using explicit variables for the created tasks:
public async Task<string> GetAsync()
{
var innerTask = InnerGetAsync();
var result = await innerTask;
var stringResult = result.ToString();
return stringResult;
}
Later you create two tasks by invoking the GetAsync() method:
var task1 = GetAsync();
var task2 = GetAsync();
At this point the two tasks are running concurrently. Each one has invoked internally the InnerGetAsync() method, which means that the two innerTasks are also up and running, and awaited. Any code that follows the await will not run before the innerTask is completed.
The completion of each outer task (task1 and task2) is dependent on the completion of its innerTask. Any code that will await task1, will also implicitly await innerTask as well.
After creating the two tasks, you combine them with Task.WhenAll, then do something else, then await the combined task, and finally process the results:
Task<string[]> whenAllTask = Task.WhenAll(task1, task2);
DoSomethingElse();
string[] results = await whenAllTask;
ProcessResults(results);
It is important that the DoSomethingElse() method will run before the two tasks are completed. So at this point three things will be happening concurrently, the DoSomethingElse() and the two active tasks. The await inside the GetAsync() method is local to this method, and does not mean that the generated outer task will be automatically awaited upon creation. To process the results of task1 and task2 you must first await them as well.

How to declare a not started Task that will Await for another Task?

I've done this Unit Test and I don't understand why the "await Task.Delay()" doesn't wait !
[TestMethod]
public async Task SimpleTest()
{
bool isOK = false;
Task myTask = new Task(async () =>
{
Console.WriteLine("Task.BeforeDelay");
await Task.Delay(1000);
Console.WriteLine("Task.AfterDelay");
isOK = true;
Console.WriteLine("Task.Ended");
});
Console.WriteLine("Main.BeforeStart");
myTask.Start();
Console.WriteLine("Main.AfterStart");
await myTask;
Console.WriteLine("Main.AfterAwait");
Assert.IsTrue(isOK, "OK");
}
Here is the Unit Test output :
How is this possible an "await" doesn't wait, and the main thread continues ?
new Task(async () =>
A task does not take a Func<Task>, but an Action. It will call your asynchronous method and expect it to end when it returns. But it does not. It returns a task. That task is not awaited by the new task. For the new task, the job is done once the method returned.
You need to use the task that already exists instead of wrapping it in a new task:
[TestMethod]
public async Task SimpleTest()
{
bool isOK = false;
Func<Task> asyncMethod = async () =>
{
Console.WriteLine("Task.BeforeDelay");
await Task.Delay(1000);
Console.WriteLine("Task.AfterDelay");
isOK = true;
Console.WriteLine("Task.Ended");
};
Console.WriteLine("Main.BeforeStart");
Task myTask = asyncMethod();
Console.WriteLine("Main.AfterStart");
await myTask;
Console.WriteLine("Main.AfterAwait");
Assert.IsTrue(isOK, "OK");
}
The problem is that you are using the non-generic Task class, that is not meant to produce a result. So when you create the Task instance passing an async delegate:
Task myTask = new Task(async () =>
...the delegate is treated as async void. An async void is not a Task, it cannot be awaited, its exception cannot be handled, and it's a source of thousands of questions made by frustrated programmers here in StackOverflow and elsewhere. The solution is to use the generic Task<TResult> class, because you want to return a result, and the result is another Task. So you have to create a Task<Task>:
Task<Task> myTask = new Task<Task>(async () =>
Now when you Start the outer Task<Task> it will be completed almost instantly because its job is just to create the inner Task. You'll then have to await the inner Task as well. This is how it can be done:
myTask.Start(TaskScheduler.Default);
Task myInnerTask = await myTask;
await myInnerTask;
You have two alternatives. If you don't need an explicit reference to the inner Task then you can just await the outer Task<Task> twice:
await await myTask;
...or you can use the built-in extension method Unwrap that combines the outer and the inner tasks into one:
await myTask.Unwrap();
This unwrapping happens automatically when you use the much more popular Task.Run method that creates hot tasks, so the Unwrap is not used very often nowadays.
In case you decide that your async delegate must return a result, for example a string, then you should declare the myTask variable to be of type Task<Task<string>>.
Note: I don't endorse the use of Task constructors for creating cold tasks. As a practice is generally frowned upon, for reasons I don't really know, but probably because it is used so rarely that it has the potential of catching other unaware users/maintainers/reviewers of the code by surprise.
General advice: Be careful everytime you are supplying an async delegate as an argument to a method. This method should ideally expect a Func<Task> argument (meaning that understands async delegates), or at least a Func<T> argument (meaning that at least the generated Task will not be ignored). In the unfortunate case that this method accepts an Action, your delegate is going to be treated as async void. This is rarely what you want, if ever.
[Fact]
public async Task SimpleTest()
{
bool isOK = false;
Task myTask = new Task(() =>
{
Console.WriteLine("Task.BeforeDelay");
Task.Delay(3000).Wait();
Console.WriteLine("Task.AfterDelay");
isOK = true;
Console.WriteLine("Task.Ended");
});
Console.WriteLine("Main.BeforeStart");
myTask.Start();
Console.WriteLine("Main.AfterStart");
await myTask;
Console.WriteLine("Main.AfterAwait");
Assert.True(isOK, "OK");
}

not understanding of async await concept [duplicate]

This question already has an answer here:
Async Await Running Synchronously
(1 answer)
Closed 2 years ago.
I do not understand when I read the Microsoft documents about async and await working, as it says
Async methods are intended to be non-blocking operations. An await
expression in an async method doesn’t block the current thread while
the awaited task is running. Instead, the expression signs up the rest
of the method as a continuation and returns control to the caller of
the async method.
so consider my code:
public Form1()
{
InitializeComponent();
AsyncTest();
Test(1);
}
private async void AsyncTest()
{
HttpClient client = new HttpClient();
var ss = TT(2);
var s = await ss;
for (int i = 0; i < 10; i++)
{
//loop
}
}
private async Task<int> TT(int i)
{
while (true)
{
if (i > 1000000000)
break;
i++;
}
return i;
}
}
when ever I call AsyncTest(), it just waits behind TT method as it specified with await key, but what does it mean it return control to the main caller??
it is not been returned to the form1, if TT is an infinite loop it just remains forever.
thanks
It seems to me you are a newbee to async-await. You violate several rules about how to use async-await.
To make a function async do the following:
Declare the function async
Return Task<TResult> instead of TResult; return Task instead of void
ONLY EXCEPTION: event handlers return void instead of Task
there should be one await inside your async function. Your compiler will warn you if you omit this.
Make sure that all async functions that you call are awaited for before you return
Guideline: let async be a suffix to your async functions. This allows you to create a sync and an async function that do the same: void MyTest() and Task MyTestAsync()
Following these rules, you can't call an async function from within a constructor. As you are using Winforms your code could be like this:
public Form1()
{
InitializeComponent();
}
public async void OnButton1_Clicked(object sender, ...)
{ // because this is an event handler the return value is void
// Start the first test and await until ready
await TestAsync();
// Start the second test and await until ready:
int i = await TestAsync(1);
}
If you call an async function you know that somewhere inside this function is an await. If you have meaningful things to do while the inner function is awaiting, do not await yet. Do your useful things after the call and await the returned Task as soon as you need the result:
public async void OnButton2_Clicked(object sender, ...)
{
// Start a test, but don't await yet
var task1 = TestAsync(1);
// if here, function TestAsync is awaiting somewhere
// it is not finished yet, so you can't use the result of it
// But you can do Something else:
DoSomethingElse();
// if here you really need the result of TestAsync, await for it
int i = await task1;
// if here you know that TestAsync is finished, i has the proper return value
}
You don't have to start and await the tasks one by one, you can start several tasks and await for them as soon as you need the results:
public async void OnButton3_Clicked(object sender, ...)
{
// start several tasks. Don't await yet:
var task1 = TestAsync(1);
var task2 = TestAsync(2);
var task3 = TestAsync(3);
DoSomethingElse();
// if here I need the results of all three tasks:
var myTasks = new Task[] {task1, task2, task3};
await Task.WhenAll(myTasks);
}
An article that really helped me understanding async-await is this interview with Eric Lippert, where he compares async-await with a cook making breakfast. If a cook has to wait for the water to boil, he doesn't wait idly, but looks around to see if he can do other things. Search somewhere in the middle for async-await.
Another article that helped me following the guidelines of async-await is Async and Await by the ever so helpful Stephen Cleary

Execute list of async tasks

I have a list of async functions I want to execute in order. When I run the following code I get the output:
Task 1 before
Task 2 before
Finished tasks
Why are my async functions not being awaited correctly?
[Test]
public async Task AsyncTaskList()
{
var data = "I'm data";
var tasks = new List<Func<object, Task>>() {Task1, Task2};
tasks.ForEach(async task =>
{
await task(data);
});
Debug.WriteLine("Finished tasks");
}
private static async Task Task1(object data)
{
Debug.WriteLine("Task 1 before");
await Task.Delay(1000);
Debug.WriteLine("Task 1 after");
}
private static async Task Task2(object data)
{
Debug.WriteLine("Task 2 before");
await Task.Delay(1000);
Debug.WriteLine("Task 2 after");
}
Because the await inside your ForEach delegate actually completes after the method exits. Change it to an actual foreach loop and awaiting will work as expected.
ForEach has no specific handling for Func<Task> (few delegate-accepting methods in the Base Class Library do, and you should note that they will almost invariably return a Task themselves). ForEach will only run the synchronous portion of your lambda - and that is the portion preceding the first await of a Task which does not complete synchronously (which is Task.Delay in your case). This is why you're seeing the "before" messages popping up at the expected time. As soon as your delegate hits await Task.Delay, the the rest of your lambda is scheduled to run sometime in the future and ForEach moves on to the next item in the list. The scheduled task continuations will then run unobserved and complete later.

How to wait for async void to complete?

i need to wait for my async void to finish. It has some await Task.Delay functions in it and I need them to finish before it continues. This void doesn't return anything so I don't see a reason to make it async Task<> and I also shuffle an array inside this void and I declare some variables. So how can I do this ? Is async Task<> the only way ? My void is not expecting any parameters/inputs.
Return Task (not Task<T>) instead of void. Note, the non-generic form of Task does not need to wrap a value. It's just for signalling completion or errors, not results. In the case of async methods, this is equivalent to a synchronous void return type. It means you can wait for your method to complete.
Every async function should return Task instead of void and Task<TResult> instead of TResult. The only exception of this is the event handler.
So if your async function isn't an event handler it should return Task. Once this is done you can await for it...
... ehm, provided that your function is also declared async and returns Task or Task<TResult>, which makes that your clients all must be async.
If you have a sync function and want to call an async function use the following:
var myTask = Task.Run( () => SomeAsyncFunction(...));
// while my task is running you can do other things
// after a while you need the result:
Task.Wait();
// only if SomeAsyncFunction returns Task<TResult>:
TResult result = Task.Result();
Be aware though that this will halt your function until the task is finished. So when possible consider making your function also async. The code would be like:
public async void Button1_Clicked(object Sender, ...)
{
var task = MySlowMultiplier(3, 4);
// while task running you can do other things
// the UI remains responsive
// after a while:
int result = await task;
ProcessResult(result);
}
private async Task<int> MySlowMultiplier(int x, int y)
{
// I'm really slow:
await Task.Delay(TimeSpan.FromSeconds(5));
return x * y;
}
You can use this code.
var result = Task.Run(async() => { return await METHODNAMEHERE(); }).Result;

Categories