How to wait the result of async operations without await? - c#

void A()
{
foreach (var document in documents)
{
var res = records.BulkWriteAsync(operationList, writeOptions); // res is Task<BulkWriteResult<JobInfoRecord>>
}
}
After foreach I would like to wait the result of all BulkWriteAsync, how to do this? I don't want to mark A() as async and do the following
await records.BulkWriteAsync(operationList, writeOptions);
Is it good solution?
void A()
{
var tasks = new List<Task<BulkWriteResult<JobInfoRecord>>>();
foreach (var document in documents)
{
var task = records.BulkWriteAsync(operationList, writeOptions);
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
I call A() in try catch if I will mark public async void A() as async I never be in catch

Well, first you want a Task that represents all the operations. The simplest way to do this is with a bit of LINQ:
Task.WhenAll(documents.Select(i => records.BulkWriteAsync(...)));
Then, you ideally want to await that task. If that isn't possible, you can try
task.GetAwaiter().GetResult();
However, make sure that none of the tasks have thread affinity - that's a great way to get a deadlock. Waiting for a task on the UI thread while the task itself needs the UI thread is a typical example.
The whole point of await is that it allows you to handle asynchronous code as if it were synchronous. So from the outside, it appears as if you never left the method until you actually get to a return (or the end of the method). For this to work, however, your method must return a Task (or Task<T>), and the callee must await your method in turn.
So a code like this:
try
{
tasks = Task.WhenAll(documents.Select(i => ...));
await tasks;
}
catch (Exception ex)
{
// Handle the exception
}
will appear to run completely synchronously, and all exceptions will be handled as usual (though since we're using Task.WhenAll, some will be wrapped in AggregateException).
However, this isn't actually possible to handle with the way .NET and C# is built, so the C# compiler cheats - await is basically a return that gives you a promise of the result you'll get in the future. And when that happens, the control returns back to where the await left the last time. Task is that promise - if you use async void, there's no way for the callee to know what's happening, and it has no option but to continue as if the asynchronous method was a run-and-forget method. If you use async Task, you can await the async method and everything "feels" synchronous again. Don't break the chain, and the illusion is perfect :)

Related

Async method that returns result and Task

I'm trying to design a public method that returns a quick result and, if needed, kicks off a long-running background task.
The result is retrieved within the method pretty quickly, and once it is retrieved it needs to be immediately available to the caller, without waiting for the potential background task to complete.
The background task is either not needed at all, or it must run for a significant amount of time - longer than the caller and all other logic in the program would take to complete without it.
Looking through the MS docs, the best design option I can come up with is to return two things from this method: the result, as well as a Task for the background task.
I don't see a better way to do this, but there are some downsides: first, the responsibility for making sure the background task completes falls on the caller of the method, even though the caller really just wants to consume the immediate result and not be concerned with the behind-the-scene stuff. Second, it is awkward to return null if the background task isn't needed to begin with: now the caller must ensure the task isn't null, in addition to making sure it completes if it is null.
What other options are available for this sort of situation?
Here's an example of what my current design looks like:
public async Task<Tuple<string, Task>> GetAndSave() {
var networkResult = await GetFromNetworkAsync();
if (NeedsToSave(networkResult)) {
var saveTask = SaveToDiskAsync(networkResult);
return new Tuple<string, Task>(networkResult, saveTask);
}
else {
return new Tuple<string, Task>(networkResult, null);
}
}
first, the responsibility for making sure the background task completes falls on the caller of the method, even though the caller really just wants to consume the immediate result and not be concerned with the behind-the-scene stuff.
If it's important to make sure the background task completes then instead of returning the Task you could hand it off to another object (that has been injected into the class that has your GetAndSave method). For example:
public class Foo
{
readonly Action<Task> _ensureCompletion;
public Foo(Action<Task> ensureCompletion)
{
_ensureCompletion = ensureCompletion;
}
public async Task<string> GetAndSaveAsync() // Your method
{
string networkResult = await GetFromNetworkAsync();
if (NeedsToSave(networkResult))
{
Task saveTask = SaveToDiskAsync(networkResult);
_ensureCompletion(saveTask); // This is a synchronous call... no await keyword here. And it returns `void`
}
return networkResult;
}
Task<string> GetFromNetworkAsync() {...}
bool NeedsToSave(string x) {...}
Task SaveToDiskAsync(string x) {...}
}
Now you can inject whatever follow-up behavior you desire for the saveTask. For example, you could write stuff out to the console depending on how it goes:
async Task DoStuff()
{
var foo = new Foo(async task =>
// ^^^^^ More on this in a moment
{
try
{
await task;
Console.Writeline("It worked!");
}
catch (Exception e)
{
Console.Writeline(e.ToString());
}
});
var immediateResult = await foo.GetAndSaveAsync();
// do stuff with `immediateResult`
}
Now, the thing that might be confusing about this (and the power behind this type of solution) is how you can have a synchronous call on the one hand:
_ensureCompletion(saveTask); // This is a synchronous call... no await keyword here
...that does asynchronous things:
var foo = new Foo(async task => ...);
// ^^^^^ This statement lambda is asynchronous
(The injected delegate might even write out to the console on a different thread than whatever thread called GetAndSaveAsync()!)
There's no magic here. It all comes down to SynchronizationContext and the inner workings of async/await.
When the compiler encounters the await keyword (in an async context) then it will do a few things:
Turn everything after the await into a continuation (basically a delegate with some state)
Replace the await keyword with something like SynchronizationContext.Current.Post(continuation)
In other words, this:
async void EnsureCompletion(Task task)
{
try
{
await task;
Console.Writeline("It worked!");
}
catch (Exception e)
{
Console.Writeline(e.ToString());
}
}
...gets turned into something like this:
void EnsureCompletion(Task task)
{
try
{
task.ContinueWith(t => SynchronizationContext.Current.Post(_ =>
{
if (t.IsCompletedSuccessfully)
{
Console.Writeline("It worked!");
}
else
{
Console.Writeline(task.Exception.ToString());
}
});
}
catch (Exception e)
{
Console.Writeline(e.ToString());
}
}
As you can see, EnsureCompletion is an entirely synchronous function that does (almost) the exact same thing as its asynchronous form. Notice how it returns void and everything. This is how you can jam an asynchronous statement lambda into a delegate parameter that has a synchronous signature.
I hinted that the console writing might happen on a totally different thread. That's because async/await is orthogonal to threading. It depends on whatever the currently assigned implementation of SynchronizationContext is programmed to do. Unless you're using WPF or WinForms then by default there will be no SynchronizationContext and continuations (the things that get passed to SynchronizationContext.Post) will just be tossed over to whatever thread happens to be free in the default thread pool.
Second, it is awkward to return null if the background task isn't needed to begin with: now the caller must ensure the task isn't null, in addition to making sure it completes if it is null.
I'm of the opinion that null was a design mistake in C#. (Ask me what some other ones are :) ). If you return null from this method:
public Task DoSomethingAsync()
{
return null; // Compiles just fine
}
...then anyone who uses it will encounter a NullReferenceException when they await it.
async Task Blah()
{
await DoSomethingAsync(); // Wham! NullReferenceException :(
}
(the reason why comes back to how the compiler desugurs the await keyword)
...and it's awkward to have to check for nulls everywhere.
Much better would be to just return Task.CompletedTask as #juharr said.
But if you just hand off the background task like I showed above then you don't have to worry about this.

Async method blocking on unawaited task

In my current project, I have a piece of code that, after simplifying it down to where I'm having issues, looks something like this:
private async Task RunAsync(CancellationToken cancel)
{
bool finished = false;
while (!cancel.IsCancellationRequested && !finished)
finished = await FakeTask();
}
private Task<bool> FakeTask()
{
return Task.FromResult(false);
}
If I use this code without awaiting, I end up blocking anyway:
// example 1
var task = RunAsync(cancel); // Code blocks here...
... // Other code that could run while RunAsync is doing its thing, but is forced to wait
await task;
// example 2
var task = RunAsync(cancelSource.Token); // Code blocks here...
cancelSource.Cancel(); // Never called
In the actual project, I'm not actually using FakeTask, and there usually will be some Task.Delay I'm awaiting in there, so the code most of the time doesn't actually block, or only for a limited amount of iterations.
In unit testing, however, I'm using a mock object that does pretty much do what FakeTask does, so when I want to see if RunAsync responds to its CancellationToken getting cancelled the way I expect it to, I'm stuck.
I have found I can fix this issue by adding for example await Task.Delay(1) at the top of RunAsync, to force it to truly run asynchronous, but this feels a bit hacky. Are there better alternatives?
You have an incorrect mental picture of what await does. The meaning of await is:
Check to see if the awaitable object is complete. If it is, fetch its result and continue executing the coroutine.
If it is not complete, sign up the remainder of the current method as the continuation of the awaitable and suspend the coroutine by returning control to the caller. (Note that this makes it a semicoroutine.)
In your program, the "fake" awaitable is always complete, so there is never a suspension of the coroutine.
Are there better alternatives?
If your control flow logic requires you to suspend the coroutine then use Task.Yield.
Task.FromResult actually runs synchronously, as would await Task.Delay(0). If you want to actually simulate asynchronous code, call Task.Yield(). That creates an awaitable task that asynchronously yields back to the current context when awaited.
As #SLaks said, your code will run synchronously. One thing is running async code, and another thing is running parallel code.
If you need to run your code in parallel you can use Task.Run.
class Program
{
static async Task Main(string[] args)
{
var tcs = new CancellationTokenSource();
var task = Task.Run(() => RunAsync("1", tcs.Token));
var task2 = Task.Run(() => RunAsync("2", tcs.Token));
await Task.Delay(1000);
tcs.Cancel();
Console.ReadLine();
}
private static async Task RunAsync(string source, CancellationToken cancel)
{
bool finished = false;
while (!cancel.IsCancellationRequested && !finished)
finished = await FakeTask(source);
}
private static Task<bool> FakeTask(string source)
{
Console.WriteLine(source);
return Task.FromResult(false);
}
}
C#'s async methods execute synchronously up to the point where they have to wait for a result.
In your example there is no such point where the method has to wait for a result, so the loop keeps running forever and thereby blocking the caller.
Inserting an await Task.Yield() to simulate some real async work should help.

Why await an async function directly not work without assigning it to a Task variable

Typically, I do the following
public static async Task dosth()
{
List<Task> job = new List<Task>();
for (int i = 0; i < 3; i++)
{
job.Add(sleep());
}
Task.WhenAll(job.ToArray());
}
static async Task sleep()
{
await Task.Delay(1000);
Console.WriteLine("Finish new");
}
It works smoothly, no problem. But when I do a review on my own code (trying using other syntax to do the same job), I suddenly figure out the following two are different.
public static async Task dosthA()
{
//This will be working synchronously, take 3 seconds.
await sleep();
await sleep();
await sleep();
//This will be working asynchronously, take 1 second only.
Task A = sleep();
Task B = sleep();
Task C = sleep();
await A;
await B;
await C;
}
Why assigning the async function to a new variable make difference? I originally think they are the same.
Update
Why it is confusing me is, actually in Microsoft doc on Async-await,
They stated the following in their code.
// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();
They are actually different, why they use //or , in a single statement, just because it makes no different in their own example?
This is because when you are returning a running Task when you call Sleep() even when you're assigning to a variable.
The confusion is that the Task does not begin if you assign it to a variable (A, B, or C) until you call await A; but that's not true. As soon as you assign sleep(); to A, sleep() was called; therefore the Task in the sleep() method is running. Assigning it to a variable or not the Task begins when you call the method; because in the method you start the Task.
Knowing this; when you call:
await A;
await B;
await C;
A, B, and C, have already starting simultaneously... After awaiting A it is most likely B, and C have also completed or are milliseconds from completing.
There are situations where you can reference a Task that hasn't started yet but you would have to purposely return a non-running Task to do that.
To answer the edit to your question also.
Tasks have a method called GetAwaiter() which returns a TaskAwaiter. In C# when you write var task = sleep(); then you're assigning the actual Task to the task variable. All the same when you write await sleep(); the compiler does some cool stuff and it actually calls the Task.GetAwaiter() method; which is subscribed to. The Task will run and when it is complete the TaskAwaiter fires the continuation action. This can't be explained in a simple answer but to know the outer logic helps.
Among other things the TaskAwaiter implements ICriticalNotifyCompletion which in turn implements INotifyCompletion. Both have one method each, OnCompleted(Action) and UnsafeOnCompleted(Action) (you can guess which is which by naming convention).
Another thing to note is that Task.GetAwaiter() returns a TaskAwaiter but Task<TResult>.GetAwaiter() returns a TaskAwaiter<TResult>. There's not a strong difference in the two but there is a difference in the GetResult() method of the two tasks; which is what's called while marshalling back to the proper threading context. The TaskAwaiter.GetResult() returns void and the TaskAwaiter<TResult>.GetResult() returns TResult.
I feel like if I push further into this I'll have to write pages to explain it all in detail... Hopefully just explaining your question and pulling the curtain back a little bit will shed enough light to help you both understand and dig deeper if you're more curious.
Ok, so based on the comment below I want to describe my answer a little bit further.
I'll start this simple; let's just make a Task; one that isn't running, and look at it first.
public Task GetTask()
{
var task = new Task(() => { /*some work to be done*/ });
//Now we have a reference to a non-running task.
return task;
}
We can now call code like:
public async void DoWork()
{
await GetTask();
}
… but we'll be waiting forever; until the application ends, because the Task was never started. However; we could do something like this:
public async void DoWork()
{
var task = GetTask();
task.Start();
await task;
}
… and it will await the running Task and continue once the Task is complete.
Knowing this you can make as many calls to GetTask() as you like and you'll only be referencing Tasks that have not started.
In your code it's just the opposite, which is fine, as this is the most used way. I encourage you to make sure your method names notify the user of how you're returning the Task. If the Task is already running the most common convention is the end the method name with Async. Here's another example doing it with a running Task for clarity.
public Task DoTaskAsync()
{
var task = Task.Run(() => { /*some work to be done*/ });
//Now we have a reference to a task that's already running.
return task;
}
And now we will most likely call this method like:
public async void DoWork()
{
await DoTaskAsync();
}
However; note that if we simply want to reference the Task just like we did earlier, we can, the only difference is this Task is running where the one prior was not. So this code is valid.
public async void DoWork()
{
var task = DoTaskAsync();
await task;
}
The big take away is how C# handles the async / await keywords. async tells the compiler that the method is going to become a continuation of a Task. In short; the compiler knows to look for all await calls and put the rest of the method in a continuation.
The await keyword tells the compiler to call the Task.GetAwaiter() method on the Task ( and basically subscribe to the INotifyCompletion and ICriticalNotifyCompletion) to signal the continuation in the method.
And this I wanted to add just incase you weren't aware. If you do have more than one task that you want to await but would rather await one task as if they were all one then you can do that with Task.WhenAll() So instead of:
var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await taskA;
await taskB;
await taskC;
You could write it a little cleaner like so:
var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await Task.WhenAll(taskA, taskB, taskC);
And there are more ways of doing this sort of thing built in; just explore it.

How do I create a Task that uses await inside the body that behaves the same as the synchronous version when Wait is called?

I have some code that creates a task that does some slow work like this:
public static Task wait1()
{
return new Task(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(10000);
Console.WriteLine("Done!");
});
}
In the real implementation, the Thread.Sleep will actually be a web service call. I would like to change the body of the method can use await (so it does not consume a thread during the network access/sleep). My first attempt (based on shotgun-debugging the compile errors) was this:
public static Task wait2()
{
return new Task(async () =>
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
});
}
However; this task doesn't seem to behave the same as the first one, because when I call .Wait() on it; it returns immediately.
Below is a full sample (console app) showing the differences (the app will end immediately when the second task starts).
What do I need to do so that I can call Start and Wait on a Task which happens to have code using await inside it? The tasks are queued and executed later by an agent, so it's vital that the task is not auto-started.
class Program
{
static void Main(string[] args)
{
var w1 = wait1();
w1.Start();
w1.Wait(); // This waits 110 seconds
var w2 = wait2();
w2.Start();
w2.Wait(); // This returns immediately
}
public static Task wait1()
{
return new Task(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(10000);
Console.WriteLine("Done!");
});
}
public static Task wait2()
{
return new Task(async () =>
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
});
}
}
It seems like this isn't possible! See alexm's answer here:
Tasks returned by async methods are always hot i.e. they are created in Running state.
:-(
I've worked around this by making my agent queue Func<Task>s instead, and the overload that receives a task simply queues () => task. Then; when de-queing a task, I check if it's not running, and if so, start it:
var currentTask = currentTaskFunction();
if (currentTask.Status == TaskStatus.Created)
currentTask.Start();
It seems a little clunky to have to do this (if this simple workaround works; why the original restriction on async methods always being created hot?), but it seems to work for me :-)
You could write this as:
public static async Task Wait2()
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
}
In general, it's rarely a good idea to ever use new Task or new Task<T>. If you must launch a task using the ThreadPool instead of using the async/await language support to compose one, you should use Task.Run to start the task. This will schedule the task to run (which is important, tasks should always be "hot" by conventions).
Note that doing this will make it so you don't have to call Task.Start, as well.
To help you understand this realize that async / await essentially does not create a new thread but rather it schedules that portion of code to be ran at an available point in time.
When you create the new Task(async () => ...) you have a task that run an async method. When that inner async method hits an await the 'new Task' is considered complete because the rest of it has been scheduled. To help you understand better place some code (a lot if wanted) in the 'new Task' before the await command. It will all execute before the application terminates and once await is reached that task will believe it has completed. It then returns and exits the application.
The best way to avoid this is to not place any task or async methods inside of your task.
Remove the async keyword and the await keyword from the method and it will work as expected.
This is the same as creating a callback if you're familiar with that.
void MethodAsync(Action callback)
{
//...some code
callback?.Invoke();
}
//using this looks like this.
MethodAsync(() => { /*code to run when complete */});
//This is the same as
Task MethodAsync()
{
//... some code here
}
//using it
await MethodAsync();
/*code to run when complete */
The thing to understand is that you're creating a new task within a task basically. So the inner 'callback' is being created at the await keyword.
You're code looks like this..
void MethodAsync(Action callback)
{
//some code to run
callback?.Invoke(); // <- this is the await keyword
//more code to run.. which happens after we run whoever is
//waiting on callback
}
There's code missing obviously. If this doesn't make sense please feel free to contact me and I'll assist. async / await (meant to make things simpler) is a beast to wrap your head around at first. Afterward you get it then it'll probably be your favorite thing in c# since linq. :P
Try this:
public async static Task wait2()
{
Console.WriteLine("Waiting...");
await Task.Delay(2000);
Console.WriteLine("Done!");
}
But we aware that the task is already started so you don't have to call start:
var w2 = wait2();
//w2.Start();
w2.Wait();
I think the problem with your wait2 function is that is creating 2 task, the one in new Task(...) and another in Task.Delay(). You are waiting for the first one, but you are not waiting for the inner one.

Chaining Parallel Tasks to an End Condition or Error Condition

I'm trying to wrap my head around some of the syntax and structure for parallel tasks in C#, specifically around chaining multiple tasks and handling errors.
The specific sequence of steps I'm looking to create are:
Spawn a parallel task and immediately return to the UI.
In the parallel task:
Do Process1()
If Process1() completes without error, do Process2()
If Process2() completes without error, do Process3()
If all tasks complete without error, do SuccessCondition()
If any task resulted in an error, do ErrorCondition()
It's my understanding that I would create a Task and call ContinueWith() to chain more tasks, passing in a TaskContinuationOptions flag to determine when to do that continuation. Additionally, that success/error conditions would fall through all of the continuations to the end. So I'm currently trying this:
var task = new Task(() => Process1());
var task2 = task.ContinueWith(t => Process2(), TaskContinuationOptions.OnlyOnRanToCompletion);
var task3 = task2.ContinueWith(t => Process3(), TaskContinuationOptions.OnlyOnRanToCompletion);
var task4 = task3.ContinueWith(t => SuccessCondition(t), TaskContinuationOptions.OnlyOnRanToCompletion);
var task5 = task4.ContinueWith(t => ErrorCondition(t), TaskContinuationOptions.NotOnRanToCompletion);
task.Start();
It appears to be behaving as expected, except that within ErrorCondition() the instance of t doesn't appear to have an exception, even if I manually threw one from within, say, Process2(). Looking at the MSDN article for handling exceptions, it says to do this:
try
{
task.Wait();
}
catch (AggregateException ex)
{
// Handle exceptions from a collection on ex
}
However, I tried that and it doesn't seem to have the exceptions there either. Also, by calling .Wait() in the main thread like that, am I negating the parallelism and just blocking? It appears that way in my tests.
So I guess my question is... What's the correct way to chain dependent tasks and handle an overall success/error condition here? Or, how should exceptions thrown from within these tasks be properly caught, while still returning immediately to the UI?
Note that if you're using .NET 4.5 and can use async/await, you can do this much more cleanly:
public async Task DoProcess()
{
try
{
await Task.Run(Process1);
await Task.Run(Process2);
await Task.Run(Process3);
SuccessCondition();
}
catch (Exception ex)
{
ErrorCondition(ex);
}
}
If you launch this from the UI thread, SuccessCondition and ErrorCondition will occur on the UI Thread as well. One functional difference here is that the exception you catch will not be an AggregateException; it will instead be the actual exception thrown during the awaited call that failed.
You would need to add a continuation to all of the tasks, 1-4, with the error handling case to allow an error in any of them to call that function.
For convenience you could create a method to add the same continuation to a collection of tasks. Here's one (feel free to add others for the other overloads of ContinueWith as needed):
public static IEnumerable<Task> ContinueWith(this IEnumerable<Task> tasks
, Action<Task> continuation, TaskContinuationOptions options)
{
return tasks.Select(task => task.ContinueWith(continuation, options))
.ToList();//important for this ToList to be here;
//we want the continuations to be added now, not when the result is iterated
}
This allows you to write:
var errorTasks = new[]{task, task2, task3, task4}
.ContinueWith(ErrorCondition, TaskContinuationOptions.NotOnRanToCompletion);
Error handling for tasks is made so much easier in C# 5.0 with async methods though. It would allow you to transform your code into this:
public static async Task Foo()
{
try
{
await Task.Run(Process1())
await Task.Run(Process2())
await Task.Run(Process3())
SuccessCondition();
}
catch (SomeExceptionType ex)
{
HandleException(ex);
}
}
This functions just as you would think it does as per your requirements, which is awesome.

Categories

Resources