How do I start not-awaited background tasks in an async method? - c#

I'm struggling with how to perform some very-long-running background processing in a world of async methods.
Using the vocabulary from Stephen Cleary's blog, I'm interested in kicking off a "delegate task" after await-ing a "promise task". I want to return the promised value as soon as it's available, and let the delegate task continue on in the background.
To handle exceptions in the un-await-ed delegate task, I'll use an exception-handling continuation modeled after the one described here.
public async Task<int> ComplexProcessAsync()
{
...
int firstResult = await FirstStepAsync();
// THE COMPILER DOESN'T LIKE THIS
Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult)).LogExceptions();
return firstResult;
}
Since VeryLongSecondStep...() could take many minutes, and its results are observable purely by side-effect (e.g. records appearing in a database), I reeeaaalllyy don't want to await it.
But as pointed out, the compiler doesn't like this. It warns "Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call."
I could ignore the warning, but I get the feeling I'm not doing this in "correct" way. So what is the correct way?
Thanks!

I could ignore the warning, but I get the feeling I'm not doing this in "correct" way. So what is the correct way?
The compiler is warning you that you're not awaiting the task. From my experience, >99% of the time the compiler is correct, the code is wrong because it's missing an await. So this is one of the rare situations where you know what you're doing and want to live dangerously. :)
In this case, you can explicitly assign the task to an unused variable:
var _ = Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult));
The compiler is intelligent enough to understand that you're using this variable to silence the "missing await" warning.
On a side note, I do not recommend ContinueWith at all; in the async world, it's just a more dangerous version of await. So I'd write LogExceptions something like this:
public static async Task LogExceptions(this Task task)
{
try
{
await task.ConfigureAwait(false);
}
catch (Exception ex)
{
LogException(ex);
}
}

Since this is within an ASP.NET Web API app I suggest you use HostingEnvironment.QueueBackgroundWorkItem. Stephen Cleary also has a post about it.
HostingEnvironment.QueueBackgroundWorkItem(ct =>
{
try
{
// your code
}
catch (Exception ex)
{
// handle & log exception
}
});
https://msdn.microsoft.com/en-us/library/dn636893(v=vs.110).aspx
http://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html

The compiler is trying to protect you from mistakes because it's really easy to forget the await when you actually need to await something, but in this case you really don't need the await, so you can ignore the warning.
However, in this case I think it's a better idea to run the task on some thread from the threadpool, because if you're running it on the main thread like you're doing now, you might cause your application to be non-responsive for new incoming requests/events.

I'd like to suggest you to move VeryLongSecondStepIDontNeedToWaitFor() out of the ComplexProcessAsync() method.
ComplexProcessAsync() itself returns a value, that is being used by at least two tasks (one of them is Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult))).
You can combine these tasks via Task.WhenAny():
var result = await ComplexProcessAsync();
await Task.WhenAny(VeryLongSecondStepIDontNeedToWaitFor(firstResult), YourSecondMethod(firstResult))
where YourSecondMethod(firstResult) is the method that uses the ComplexProcessAsync() result.

Related

How to sequential multilevel async process with C#?

I have a daily scheduler sequential scenario that needs to run every midnight:
Check_Tenant_Expiry and Get its return value (true/false)
Run_Daily_Report (pass the returning value from Check_Tenant_Expiry)
I expect to do Check_Tenant_Expiry and after it completed it will continue with Run_Daily_Report, I use the code below
bool _xxx = await Check_Tenant_Expiry().ContinueWith(t =>
{
Run_Daily_Report(_xxx); // THE WARNING GOES HERE
}, TaskContinuationOptions.OnlyOnRanToCompletion);
public static async Task<bool> Check_Tenant_Expiry()
{
bool _ret = false;
...
...
return await Task.FromResult(_ret);
}
public static async Task Run_Daily_Report()
{ .... }
questions:
Why I got a warning on Run_Daily_Report:
Severity Code Description Project File Line Suppression State
Warning CS4014 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
I am not sure that I already have correct logic for the sequential multilevel process like first I need to run Check_Tenant_Expiry() until it finished completely and then continue with Run_Daily_Report()
I need advice.
Many thanks in advance
Don
I expect to do Check_Tenant_Expiry and after it completed it will continue with Run_Daily_Report
You shouldn't use ContinueWith for this. ContinueWith is a low-level method with dangerous default behavior. At the very least, you must pass a TaskScheduler. In addition, ContinueWith doesn't understand asynchronous code, so if you pass async code in, you'll also need to use Unwrap.
Instead, you should just use await, which is simpler, more maintainable, and does the right thing by default:
bool _xxx = await Check_Tenant_Expiry();
await Run_Daily_Report(_xxx);
On a side note, this seems suspect:
return await Task.FromResult(_ret);
That's literally a less efficient form of this:
return _ret;
I suspect that the await Task.FromResult was added to avoid a compiler warning telling you that the method will run synchronously. If you were in fact getting that warning, then the proper solution is to either make the method properly asynchronous (not adding an await just to make the warning go away), or to remove the async and leave the method synchronous.
Try this
await Check_Tenant_Expiry().ContinueWith( async (t) =>
{
await Run_Daily_Report();
}, TaskContinuationOptions.OnlyOnRanToCompletion);
You made a function with async keyword, which means you told compiler excute "Run_Daily_Reporty" method by asynchronously. But you didn't execute the method with await keyword, which means It's run by synchronously. That's why compiler told you "it's right?"
Try this
// You must use async keyword when you want to use await keyword in lambda function.
await Check_Tenant_Expiry().ContinueWith(async(t) =>
{
await Run_Daily_Report(t.Result);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
I think you made code the way you wanted to work.
I think you might not know the async/await keywords yet.
They can make your asynchronous code looks like synchronous code that we've been making every day, which means it's way more easy to understand.
How about making code like below. It's going to make the same result with the code you made.
bool result = await Check_Tenant_Expiry();
await Run_Daily_Report(result);

ContinueWith Method - How to understand it exactly?

So i have e.g. this code here:
initialTask.ContinueWith((argument) => { ... });
I understand the second Task is executed once the first one is finished.
I have to provide the second Task with an argument though, which is also of Type Task.
Is this 'argument' the OLD task or is this an entirely new instance?
E.g. when i want to handle a cancellation of the First Task in the Second Task, do i have to call:
initialTask.IsCanceled
or
argument.IsCanceled
?
Is this 'argument' the OLD task or is this an entirely new instance?
Yes, it's a reference to the same Task instance parameter passed as the parameter to .ContinueWith (i.e. "Old") - you can verify this as per below:
var initialTask = Task.Delay(1000);
initialTask.ContinueWith(t2 => {Debug.Assert(t2 == initialTask);}).Wait();
The reason why the task instance is passed in is to allow you to access the completed state and output of the Task. But, before accessing the result of t2, you'll need to see whether it throws an exception (t2.IsFaulted), cancelled (t2.IsCanceled) etc which becomes messy very quickly.
Instead, now that C# supports the async / await syntax, that you should find the code easier to read, and easier to handle exceptions etc, if you rewrite your code as follows:
async Task MyMethod()
{
try
{
var initialResult = await SomeInitialTask();
var secondResult = await SecondTask(initialResult); // instead of .ContinueWith and accessing t2.Result
... etc.
}
catch (Exception ex)
{
// Much easier than checking .IsFaulted on each nested task
}
}
The argument is the old task. It is provided as argument for convenience and efficiency. Without it the lambda would be forced to close over the variable initialTask in the outer scope, and closures have a memory overhead.

Waiting for a Task conditionally

For an async method that returns a Task<bool>, I need to take some actions when the method completes. The async method looks like this:
async Task<bool> EntryExists(string entry)
{
return await Task.Run(() => call_that_returns_bool());
}
I call it and attach a continuation task to it to perform follow-up actions:
EntryExists(my_entry).ContinueWith(t =>
{
if(t.Result) ...
});
However, I now need to conditionally wait for chained task to complete. Means depending upon a parameter, I either need to return immediately to the caller, or wait till the tasks finish. I change the above code to look like this:
var T = EntryExists(my_entry).ContinueWith(t =>
{
if(t.Result) ...
});
if(wait) T.Wait(); //wait is my parameter
Running this, the code gets stuck forever at T.Wait() when wait parameter is true, as if T never kicked off. I then tried the following:
var T = EntryExists(my_entry).ContinueWith(t =>
{
if(t.Result) ...
});
T.Start();
if(wait) T.Wait();
Upon which it tells me that
Start may not be called on a continuation task
I know I'm missing something basic, but after been coding for the last 15 hours, my brain isn't helping much. Can someone point out what I need to do?
You shouldn't block on async code. In short, you're very likely to be blocking the thread that the async method needs to return to (in this case you are, as it's the UI thread). The solution is to use async-await all the way through, rather than trying to run it synchronously. If you absolutely have to present a synchronous method, then at least provide as simpler wrapper as possible, rather than mixing ContinueWith and async-await.
Your outer call can be rewritten as:
async Task<{something}> MakeCallAndContinue()
{
try
{
await EntryExists(my_entry);
// additional continuation code
}
catch (Exception e)
{
// handle in some way
}
}
var task = MakeCallAndContinue();
if (wait) await task;
It's unusual to want to start a task and then not await it, which is what you're doing if wait is false. The error handler I've put in the above code is to ensure that you don't get an exception thrown on to an unawaited task. If you did that, then it would be thrown out somewhere else, and probably kill your process if you haven't declared a global handler.
You won't be able to use the above in your WPF command as-is, because it's async. However, you can have async WPF command handlers, as explained here. If you did want to do it synchronously then you would need to call .Wait(), but as Stephen Cleary explains in my first link, you have to use ConfigureAwait(false) on all awaited tasks all the way down in order to prevent one of them from trying to return to the occupied UI thread, and deadlocking.

Does the following code sequence make sense in C#

I haven't used async/await very often and I'm not fully comfortable with them. I am trying to make an operation that is synchronous to be run asynchronously. Can you tell me if the following code snippet makes sense:
public static async Task<string> FileVersionAsync(string localFilePath)
{
FileVersionInfo versionInfo;
try
{
versionInfo = await Task.FromResult(FileVersionInfo.GetVersionInfo(localFilePath));
}
catch (SecurityException)
{
return "File Version Checker does not have permission to read file version";
}
catch (FileNotFoundException)
{
return "Unable to find configured file";
}
if (versionInfo.FileVersion == null)
return "N/A";
return versionInfo.FileVersion;
}
Adding Task, await and async do not make something asynchronous; they merely provide the plumbing to work with asynchronous code when it happens. In your example: it never happens asynchronously, so all you are doing is adding plumbing overhead for no benefit.
The compiler will generate a ton of extra code that will turn out to never be hit, because when it gets to the await it'll discover that the task is already complete, and will simply continue on the existing thread.
To actually be asynchronous, you need ... something that is async. This could be external IO, or could be something threading related - but note that simply jumping to another thread doesn't buy you anything: it just adds a context switch.
If there was a FileVersionInfo.GetVersionInfoAsync method, what you are doing might be worthwhile.
No it does not make sense.
The only reason to make your function async is if somewhere inside it awaits other functions that are async. In fact your compiler warns you if you forget to await somewhere.
The async-await syntax was invented as a replacement for other task functions like Task.ContinueWith, Task.FromResult, Task.FromException etc.
In this interview Eric Lippert compared async-await with a cook who has to prepare breakfast Search somewhere in the middle for async-await.
If a cook has to prepare breakfase he starts to boil water. But instead of waiting for the water to cook, he starts slicing bread, and do other things. Only after he has nothing to do anymore he starts waiting idly for the water to boil after which he makes the tea.
Similarly: if a program has to wait for an external process to perform a request, like a database query, write data to a file, get information from the internet etc. async-await makes sure that your thread doesn't wait idly. Instead your thread goes up its call stack to see if one of the callers can continue working without the result from the other process.
You'll see this in the following code:
public async Task<string> ReadTextFile()
{
StreamReader txtReader = File.OpenText(...);
// read the complete text file; don't wait until finished yet
Task<String> taskTxt = txtReader.ReadToEndAsync();
// as we didn't use await, we can continue processing:
DoSomething();
// now we need the result from the read action: await for it
// the result of await Task<TResult> is the TResult:
string importedTxt = await taskTxt;
return importedTxt;
}
Some guidelines for async-await:
Only use async-await if you call at least one other async function
instead of void return Task, instead of TResult return Task<TResult>
Only exception: the async event handler: this async function returns void
The return of await Task is void; the return of await Task<TResult> is TResult
If you don't need the result of an async function right now, and if you can do something meaningful while the task is processing: start the task by calling the async function, but don't await for it. Only await when you need the result
.
public async void Button_Clicked(object sender, EventArgs e)
{
// indicate you will do something time consuming:
this.ProgressBar1.Visible = true;
await DoSomethingTimeconsumingAsync(...);
// finished:
this.progressBar1.Visible = false;
}
This will make sure that whenever your thread has to wait something, it can do other things, and thus your GUI remains responsive.
No. There appears to be absolutely no need to make this method async, because Task.FromResult is ready immediately.

Different exception handling between Task.Run and Task.Factory.StartNew

I encountered an issue when I was using Task.Factory.StartNew and tried to capture an exception that is thrown. In my application I have a long running task that I want to encapsulate in a Task.Factory.StartNew(.., TaskCreationOptions.LongRunning);
However, the exception isn't caught when I'm using Task.Factory.StartNew. It is however working as I expect when I use Task.Run, which I thought was just a wrapper on Task.Factory.StartNew (according to for instance this MSDN article).
A working example is provided here, the difference being that the exception is written to console when using Task.Run, but not when using Factory.StartNew.
My question would be:
if I have a LongRunning task that has the possibility to throw exceptions, how should I handle them in the calling code?
private static void Main(string[] args)
{
Task<bool> t = RunLongTask();
t.Wait();
Console.WriteLine(t.Result);
Console.ReadKey();
}
private async static Task<bool> RunLongTask()
{
try
{
await RunTaskAsync();
}
catch (Exception e)
{
Console.WriteLine(e);
return false;
}
Console.WriteLine("success");
return true;
}
private static Task RunTaskAsync()
{
//return Task.Run(async () =>
// {
// throw new Exception("my exception");
// });
return Task.Factory.StartNew(
async () =>
{
throw new Exception("my exception");
});
}
Your problem is that StartNew doesn't work like Task.Run with async delegates. The return type of StartNew is Task<Task> (which is convertible to Task). The "outer" Task represents the beginning of the method, and the "inner" Task represents the completion of the method (including any exceptions).
To get to the inner Task, you can use Unwrap. Or you can just use Task.Run instead of StartNew for async code. LongRunning is just an optimization hint and is really optional. Stephen Toub has a good blog post on the difference between StartNew and Run and why Run is (usually) better for async code.
Update from #usr comment below: LongRunning only applies to the beginning of the async method (up until the first incomplete operation is awaited). So it's almost certainly better all around to use Task.Run in this case.
I'll pull some of my comments into an answer because they turned out to be helpful:
LongRunning is identical to forcing a new thread to be created in practice. And your async method is probably not on that thread for a long time (it is taken off at the first await point). You don't want LongRunning in this case.
It does not matter how long the async method runs. The thread is destroyed at the very first await (that operates on a non-completed task).
Can the compiler use this hint in any way? The compiler is generally unable to analyze your code in any major way. Also the compiler does not know anything about the TPL. The TPL is a library. And this library will just always launch a new thread. Specify LongRunning iff your task will almost always burn 100% CPU for multiple seconds or will block for multiple seconds with very high probability.
My guess is you don't want LongRunning here because if you're blocking, why are you using async in the first place? async is about not blocking but getting off the thread.
It should be possible when you first Unwrap the task:
await RunTaskAsync().Unwrap();
Or alternatively:
await await RunTaskAsync();

Categories