How to execute some code when a block exits due to "await"? - c#

tldr: Is there a way to execute some code when an "await" causes a method call to return?
Suppose I log entry and exit of C# methods with an object whose Dispose() method logs the method's exit. For example
void DoWhatever()
{
using (LogMethodCall("DoWhatever")
{
// do whatever
}
}
That is, the method LogMethodCall() logs "DoWhatever entered" and then returns an object of type CallEnder whose Dispose() method logs "DoWhatever exiting". That works fine until await is used. For example...
async Task DoWhatever()
{
using (LogMethodCall("DoWhatever")
{
// do first part.
await Something();
// do second part.
}
}
The above code returns a Task to the caller when it hits the await, and the rest of the code (including the call to CallEnder.Dispose()) runs in that Task. My problem is that I want to log "DoWhatever exiting" when the await triggers the actual return, and not when CallEnder.Dispose() is finally called.
Is there a way to do that? Is there something like an event that's raised when await causes DoWhatever() to return? Maybe something to do with ExecutionContext or CallContext or TaskScheduler?
Note that I need to keep the "using (some_object)" pattern described in the above code. That pattern works well to log entry and exit of a block. I can change the implementation of some_object to detect when control returns from DoWhatever() to its caller, but I'd prefer not to change the implementation of DoWhatever(). Although I could if there's no other way.
ETA further clarification: I want to
Log when control exits from DoWhatever() and returns to its caller,
whether that's due to the await or due to the "natural" exit from
DoWhatever().
Do it in the same thread that called DoWhatever().
Preferably do it via the "using" clause shown above because that
pattern is already used in many places and works perfectly without
await.

Surprisingly it can be done, using AsyncLocal. AsyncLocal is like ThreadLocal except it flows through async code which might switch threads. It has a constructor which allows you to listen for value changes, and it even tells you the reason value has changed. It can be changed either because you explicitly set Value or if async context switch happens (in this case, Value changes to null/default when control leaves, and it changes back to original value when control returns). This allows us to detect when first await is reached, and not just first await but await that will introduce context switch (so, await Task.CompletedTask will not trigger context switch for example). So on first such switch Task will be returned back to caller.
Here is sample code:
public class Program {
public static void Main() {
var task = Test();
Console.WriteLine("Control flow got out of Test");
task.Wait();
}
static async Task Test() {
using (LogMethodCall()) {
await Task.Delay(1000);
Console.WriteLine("Finished using block");
}
}
static IDisposable LogMethodCall([CallerMemberName] string methodName = null) {
return new Logger(methodName);
}
private class Logger : IDisposable {
private readonly string _methodName;
private AsyncLocal<object> _alocal;
private bool _disposed;
public Logger(string methodName) {
Console.WriteLine($"{methodName} entered");
_methodName = methodName;
_alocal = new AsyncLocal<object>(OnChanged);
_alocal.Value = new object();
}
private void OnChanged(AsyncLocalValueChangedArgs<object> args) {
if (_disposed)
return;
// this property tells us that value changed because of context switch
if (args.ThreadContextChanged) {
Dispose();
}
}
public void Dispose() {
// prevent multiple disposal
if (_disposed)
return;
_disposed = true;
_alocal = null;
Console.WriteLine($"{_methodName} exited");
}
}
}
It outputs:
Test entered
Test exited
Control flow got out of Test
Finished using block
You can use the same code for regular functions too, because in them async local will never change and so dispose will happen as usual in the end of using block.

If you want to log when Something finished any synchronous actions and returns a Task, this is easy:
var task = Something();
/* log as you like */
await task;

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.

Why won't this async/await run asynchronously?

I've searched for the answer to this but according to many guides and SO questions this code still appears correct to me, yet it runs synchronously.
private void CheckConditions()
{
foreach (var obj in myObjects)
{
if (obj.ConditionMet)
{
HandleConditionAsync(obj);
}
}
DoOtherWork();
}
private async void HandleConditionAsync(MyObject obj)
{
// shouldn't control transfer back to CheckConditions() here while we wait for user input?
string userInput = await obj.MessagePromptAsync("hello user");
DoSomeBookkeeping(obj);
}
// (MyObject.cs)
private MessagePrompt messagePrompt; // inherits from UserControl
public async Task<string> MessagePromptAsync(string prompt)
{
return await Task.FromResult<string>(messagePrompt.Prompt(prompt));
}
// (MessagePrompt.cs)
public string Prompt(string prompt)
{
this.UIThread(() => this.SetMessagePrompt(prompt));
userInputAutoResetEvent.WaitOne();
return myResult; // set in a button handler that also sets the AutoResetEvent
}
I'm intending for CheckConditions() to continue along merrily but instead it is stuck on MessagePrompt's AutoResetEvent despite my async/awaits. The only thing I can figure might be wrong is that perhaps MessagePrompt's methods aren't able to run asynchronously due to some restriction from UserControl, its use of a UI thread reference, or maybe non-async methods at the top of the stack.
There's nothing in your code that's asynchronous. The only task you have, you created from a result value, meaning the Prompt() method has to complete and return its result before you'll even get the Task object back to wait on. That object will already be completed, so any await on it will complete immediately, once it has the Task to wait on.
Maybe you meant this instead:
public async Task<string> MessagePromptAsync(string prompt)
{
return await Task.Run(() => messagePrompt.Prompt(prompt));
}
Or alternatively (if you really do have nothing else in the MessagePromptAsync() method):
public Task<string> MessagePromptAsync(string prompt)
{
return Task.Run(() => messagePrompt.Prompt(prompt));
}
Note that this may lead to a different problem, depending on what DoOtherWork() and UIThread() actually do. If your UI thread gets tied up in DoOtherWork() and the UIThread() method is wrapping Dispatcher.Invoke() or similar, then you'll have a deadlock.
If that does not address your problem, please provide a good Minimal, Complete, and Verifiable code example that reliably reproduces the problem.
You need to make CheckConditions() async as well, and then await the call to HandleConditionAsync(MyObject obj). CheckConditions() runs synchronously in your sample.
private async Task CheckConditionsAsync()
{
foreach (var obj in myObjects)
{
if (obj.ConditionMet)
{
await HandleConditionAsync(obj);
}
}
DoOtherWork();
}
Also, and this is just a best practices thing, an async method should always return a Task when possible. The only time I've ever had to use async void is for compatibility with an event handler. You can see I've changed CheckConditions() this way, and HandleConditionAsync(MyObject obj) should be modified similarly. I also changed the method name to represent it's asynchronous behaviour.
If you need to run a method that returns a Task synchronously (and you shouldn't do this, this is an indication of something incorrect about your design), you can run it with Task.FromResult(MyMethodAsync()). Again, avoid doing this wherever you can, it defeats the purpose of making a method asynchronous to in the first place.

Creating an async webservice method

I've tried to read up on async methods and am now trying to create my own async method. The method is a webservice call that returns a list of error logs. I'm not sure that I've understood correctly so I thought I'd share my code to see if I should do anything different.
All I want the code to do is return a list of errorlogs by calling a method GetAllErrorLogs(), that is a synchronized method. Since it can take a second to fetch all the error logs I want to have the opportunity to do other stuff once I called the GetAllErrorLogs() method. Here is the code.
[WebMethod]
public async Task<List<ErrorLog>> GetAllErrorLogs()
{
List<ErrorLog> errorLogs = new List<ErrorLog>();
await System.Threading.Tasks.Task.Run(() => {
errorLogs = ErrorLogRepository.GetAllErrorLogs();
});
if (errorLogs == null)
return new List<ErrorLog>();
return errorLogs;
}
Thanks!
I recently gave a talk at ThatConference on async on the server side, and I address this issue in the slides.
On the server side, you want to avoid the use of Task.Run and other constructs that queue work to the thread pool. As much as possible, keep thread pool threads available for handling requests.
So, ideally your repository would have an asynchronous method GetAllErrorLogsAsync, which would itself be asynchronous. If GetAllErrorLogs cannot be asynchronous, then you may as well just call it directly (removing the await Task.Run).
Since it can take a second to fetch all the error logs I want to have the opportunity to do other stuff once I called the GetAllErrorLogs() method.
If you have a GetAllErrorLogsAsync available, then this can easily be done using Task.WhenAll. However, if GetAllErrorLogs is synchronous, then you can only do this by doing parallel work in your request (e.g., multiple calls to Task.Run followed by Task.WhenAll).
Parallel code on the server must be approached with great trepidation. It is only acceptable in a very limited set of scenarios. The entire point of async on the server side is to use fewer threads per request, and when you start parallelizing, you're doing the opposite: multiple threads per request. This is only appropriate if you know your user base is very small; otherwise, you'll kill your server scalability.
I found this great codeproject detailed article about how to achieve that
http://www.codeproject.com/Articles/600926/Asynchronous-web-services-call-in-ASP-NET
**This is potentially wrong, read comments or spinoff question at HttpContext.Current after an await
If ErrorLogRepository.GetAllErrorLogs() is not thread-safe, it will cause weird bugs and potentially exception out. Make sure your code is ready for multi-threaded operation before switching to async methods, this is obviously very trivial advice but often overlooked. For example, if you reference HttpContext.Current in your methods, your code will die in the async method, and sometimes even AFTER the await. The reason is that the code within the async block will potentially be run on a separate thread, which will not have access to the same HttpContext.Current thread-static property, and await gets compiled into two methods. All code before an await gets run on one thread, and then calls the code after an await keyword as a continuation, but potentially on yet another thread. So sometimes your code will even work in an async block, only to choke unexpectedly after it gets "out" of the async back to what you think is a synchronous part of your code (but in reality everything after an await keyword is already not guaranteed to be the original thread).
Here is some production code...
using System.Web.Http;
using AysncTask = System.Threading.Tasks.Task;
public class myController : ApiControllerBase
{
[HttpPut]
[Route("api/cleardata/{id}/{requestId}/")]
public async AysncTask ClearData(Guid id, Guid requestId)
{
try
{
await AysncTask.Run(() => DoClearData(id, requestId));
}
catch (Exception ex)
{
throw new Exception("Exception in myController.ClearData", ex);
}
}
}
Handling Async exceptions is also VERY VERY important.. although this is for a windows console app, the same principles should apply.
source: https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncAndExceptions
{
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += (s, e) => Log("*** Crash! ***", "UnhandledException");
TaskScheduler.UnobservedTaskException += (s, e) => Log("*** Crash! ***", "UnobservedTaskException");
RunTests();
// Let async tasks complete...
Thread.Sleep(500);
GC.Collect(3, GCCollectionMode.Forced, true);
}
private static async Task RunTests()
{
try
{
// crash
// _1_VoidNoWait();
// crash
// _2_AsyncVoidAwait();
// OK
// _3_AsyncVoidAwaitWithTry();
// crash - no await
// _4_TaskNoWait();
// crash - no await
// _5_TaskAwait();
// OK
// await _4_TaskNoWait();
// OK
// await _5_TaskAwait();
}
catch (Exception ex) { Log("Exception handled OK"); }
// crash - no try
// await _4_TaskNoWait();
// crash - no try
// await _5_TaskAwait();
}
// Unsafe
static void _1_VoidNoWait()
{
ThrowAsync();
}
// Unsafe
static async void _2_AsyncVoidAwait()
{
await ThrowAsync();
}
// Safe
static async void _3_AsyncVoidAwaitWithTry()
{
try { await ThrowAsync(); }
catch (Exception ex) { Log("Exception handled OK"); }
}
// Safe only if caller uses await (or Result) inside a try
static Task _4_TaskNoWait()
{
return ThrowAsync();
}
// Safe only if caller uses await (or Result) inside a try
static async Task _5_TaskAwait()
{
await ThrowAsync();
}
// Helper that sets an exception asnychronously
static Task ThrowAsync()
{
TaskCompletionSource tcs = new TaskCompletionSource();
ThreadPool.QueueUserWorkItem(_ => tcs.SetException(new Exception("ThrowAsync")));
return tcs.Task;
}
internal static void Log(string message, [CallerMemberName] string caller = "")
{
Console.WriteLine("{0}: {1}", caller, message);
}
}
}

warning this call is not awaited, execution of the current method continues

Just got VS2012 and trying to get a handle on async.
Let's say I've got an method that fetches some value from a blocking source. I don't want caller of the method to block. I could write the method to take a callback which is invoked when the value arrives, but since I'm using C# 5, I decide to make the method async so callers don't have to deal with callbacks:
// contrived example (edited in response to Servy's comment)
public static Task<string> PromptForStringAsync(string prompt)
{
return Task.Factory.StartNew(() => {
Console.Write(prompt);
return Console.ReadLine();
});
}
Here's an example method that calls it. If PromptForStringAsync wasn't async, this method would require nesting a callback within a callback. With async, I get to write my method in this very natural way:
public static async Task GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
Console.WriteLine("Welcome {0}.", firstname);
string lastname = await PromptForStringAsync("Enter your last name: ");
Console.WriteLine("Name saved as '{0} {1}'.", firstname, lastname);
}
So far so good. The problem is when I call GetNameAsync:
public static void DoStuff()
{
GetNameAsync();
MainWorkOfApplicationIDontWantBlocked();
}
The whole point of GetNameAsync is that it's asynchronous. I don't want it to block, because I want to get back to the MainWorkOfApplicationIDontWantBlocked ASAP and let GetNameAsync do its thing in the background. However, calling it this way gives me a compiler warning on the GetNameAsync line:
Warning 1 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'm perfectly aware that "execution of the current method continues before the call is completed". That's the point of asynchronous code, right?
I prefer my code to compile without warnings, but there's nothing to "fix" here because the code is doing exactly what I intend it to do. I can get rid of the warning by storing the return value of GetNameAsync:
public static void DoStuff()
{
var result = GetNameAsync(); // supress warning
MainWorkOfApplicationIDontWantBlocked();
}
But now I have superfluous code. Visual Studio seems to understand that I was forced to write this unnecessary code, because it suppresses the normal "value never used" warning.
I can also get rid of the warning by wrapping GetNameAsync in a method that's not async:
public static Task GetNameWrapper()
{
return GetNameAsync();
}
But that's even more superfluous code. So I have to write code I don't need or tolerate an unnecessary warning.
Is there something about my use of async that's wrong here?
If you really don't need the result, you can simply change the GetNameAsync's signature to return void:
public static async void GetNameAsync()
{
...
}
Consider to see answer to a related question:
What's the difference between returning void and returning a Task?
Update
If you need the result, you can change the GetNameAsync to return, say, Task<string>:
public static async Task<string> GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
string lastname = await PromptForStringAsync("Enter your last name: ");
return firstname + lastname;
}
And use it as follows:
public static void DoStuff()
{
Task<string> task = GetNameAsync();
// Set up a continuation BEFORE MainWorkOfApplicationIDontWantBlocked
Task anotherTask = task.ContinueWith(r => {
Console.WriteLine(r.Result);
});
MainWorkOfApplicationIDontWantBlocked();
// OR wait for the result AFTER
string result = task.Result;
}
I'm quite late to this discussion, but there is also the option to use the #pragma pre-processor directive. I have some async code here and there that I explicitly do not want to await in some conditions, and I dislike warnings and unused variables just like the rest of you:
#pragma warning disable 4014
SomeMethodAsync();
#pragma warning restore 4014
The "4014" comes from this MSDN page: Compiler Warning (level 1) CS4014.
See also the warning/answer by #ryan-horath here https://stackoverflow.com/a/12145047/928483.
Exceptions thrown during an async call that is not awaited will be lost. To get rid of this warning, you should assign the Task return value of the async call to a variable. This ensures you have access to any exceptions thrown, which will be indicated in the return value.
Update for C# 7.0
C# 7.0 adds a new feature, discard variables: Discards - C# Guide, which can also help in this regard.
_ = SomeMethodAsync();
I'm not particularly fond of the solutions that either assign the task to an unused variable, or changing the method signature to return void. The former creates superfluous, non-intuitive code, while the latter may not be possible if you're implementing an interface or have another usage of the function where you want to use the returned Task.
My solution is to create an extension method of Task, called DoNotAwait() that does nothing. This will not only suppress all warnings, ReSharper or otherwise, but makes the code more understandable, and indicates to future maintainers of your code that you really intended for the call to not be awaited.
Extension method:
public static class TaskExtensions
{
public static void DoNotAwait(this Task task) { }
}
Usage:
public static void DoStuff()
{
GetNameAsync().DoNotAwait();
MainWorkOfApplicationIDontWantBlocked();
}
Edited to add: this is similar to Jonathan Allen's solution where the extension method would start the task if not already started, but I prefer to have single-purpose functions so that the caller's intent is completely clear.
async void IS BAD!
What's the difference between returning void and returning a Task?
https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html
What I suggest is that you explicitly run the Task via an anonymous method...
e.g.
public static void DoStuff()
{
Task.Run(async () => GetNameAsync());
MainWorkOfApplicationIDontWantBlocked();
}
Or if you did want it to block you can await on the anonymous method
public static void DoStuff()
{
Task.Run(async () => await GetNameAsync());
MainWorkOfApplicationThatWillBeBlocked();
}
However, if your GetNameAsync method has to interact with UI or even anything UI bound, (WINRT/MVVM, I'm looking at you), then it gets a little funkier =)
You'll need to pass the reference to the UI dispatcher like this...
Task.Run(async () => await GetNameAsync(CoreApplication.MainView.CoreWindow.Dispatcher));
And then in your async method you'll need to interact with your UI or UI bound elements thought that dispatcher...
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { this.UserName = userName; });
This is what I'm currently doing:
SomeAyncFunction().RunConcurrently();
Where RunConcurrently is defined as...
/// <summary>
/// Runs the Task in a concurrent thread without waiting for it to complete. This will start the task if it is not already running.
/// </summary>
/// <param name="task">The task to run.</param>
/// <remarks>This is usually used to avoid warning messages about not waiting for the task to complete.</remarks>
public static void RunConcurrently(this Task task)
{
if (task == null)
throw new ArgumentNullException("task", "task is null.");
if (task.Status == TaskStatus.Created)
task.Start();
}
https://github.com/docevaad/Anchor/blob/master/Tortuga.Anchor/Tortuga.Anchor.source/shared/TaskUtilities.cs
https://www.nuget.org/packages/Tortuga.Anchor/
According to the Microsoft article on this warning, you can solve it by simply assigning the returned task to a variable. Below is a translation of the code provided in the Microsoft example:
// To suppress the warning without awaiting, you can assign the
// returned task to a variable. The assignment doesn't change how
// the program runs. However, the recommended practice is always to
// await a call to an async method.
// Replace Call #1 with the following line.
Task delayTask = CalledMethodAsync(delay);
Note that doing this will result in the "Local variable is never used" message in ReSharper.
Here, a simple solution.
public static class TasksExtensions
{
public static void RunAndForget(this Task task)
{
}
}
Regards
If you don't want to change the method signature to return void (as returning void should always be avoided), you can use C# 7.0+ Discard feature like this, which is slightly better than assigning to a variable (and should remove most other source validation tools warnings):
public static void DoStuff()
{
_ = GetNameAsync(); // we don't need the return value (suppresses warning)
MainWorkOfApplicationIDontWantBlocked();
}
It's your simplified example that causes the superflous code. Normally you would want to use the data that was fetched from the blocking source at some point in the program, so you would want the result back so that it would be possible to get to the data.
If you really have something that happens totally isolated from the rest of the program, async would not be the right approach. Just start a new thread for that task.
Do you really want to ignore the result? as in including ignoring any unexpected exceptions?
If not you might want a look at this question: Fire and Forget approach,
Task tResult = GetNameAsync();
this will not make any errors and no need to make the caller method as asynch.

async ctp recursion

I'm about 15 minutes into my first play with the async CTP... (nice).
Here's a really simple server I've knocked together:
internal class Server
{
private HttpListener listener;
public Server()
{
listener = new HttpListener();
listener.Prefixes.Add("http://*:80/asynctest/");
listener.Start();
Go();
}
async void Go()
{
HttpListenerContext context = await listener.GetContextAsync();
Go();
using (var httpListenerResponse = context.Response)
using (var outputStream = httpListenerResponse.OutputStream)
using (var sw = new StreamWriter(outputStream))
{
await sw.WriteAsync("hello world");
}
}
}
As can be seen, the async method Go calls itself. In classic non-async world, this would cause a stack overflow. I assume that this isn't the case with an async method, but I'd like to be sure, one way or the other. Anyone?
Let's break it down into something simpler:
async static void Go()
{
await Something();
Go();
await SomethingElse();
}
How does the compiler deal with this?
Basically this becomes something like this sketch:
class HelperClass
{
private State state = STARTSTATE;
public void DoIt()
{
if (state == STARTSTATE) goto START;
if (state == AFTERSOMETHINGSTATE) goto AFTERSOMETHING;
if (state == AFTERSOMETHINGELSESTATE) goto AFTERSOMETHINGELSE;
START:
{
state = AFTERSOMETHINGSTATE;
var awaiter = Something().MakeAnAwaiter();
awaiter.WhenDoneDo(DoIt);
return;
}
AFTERSOMETHING:
{
Go();
state = AFTERSOMETHINGELSESTATE;
var awaiter = SomethingElse().MakeAnAwaiter();
awaiter.WhenDoneDo(DoIt);
return;
}
AFTERSOMETHINGELSE:
return;
}
static void Go()
{
var helper = new HelperClass();
helper.DoIt();
}
Now all you have to remember is that when each asynchronous operation completes, "DoIt" is scheduled to be called again by the message loop (on the appropriate instance of the helper of course).
So what happens? Work it out. You call Go for the first time. That makes helper number one and calls DoIt. That calls Something(), gets a task back, makes an awaiter for that task, tells the awaiter "when you're done, call helper1.DoIt" and returns.
A tenth of a second later the task completes and the message loop calls helper1's DoIt. helper1's state is AFTERSOMETHINGSTATE, so we take the goto and call Go. That makes helper2 and calls DoIt on that. That calls Something(), gets a task back, makes an awaiter for that task, tells the awaiter "when you're done, call DoIt on helper2" and returns control back to helper1's DoIt. That calls SomethingElse, makes an awaiter for that task, and tells it "when you're done doing something else, call helper1's DoIt". It then returns.
Now we have two tasks outstanding and no code on the stack. One of the tasks will complete first. Suppose the SomethingElse task completes first. The message loop calls helper1.DoIt(), which immediately returns. Helper1 is now garbage.
Later the message loop calls helper2.DoIt(), and branches to AFTERSOMETHING. Now Go() is called, which creates helper3...
So no, there's no unbounded recursion here. Every time Go executes it runs as far as asynchronously starting Something() and then it returns to its caller. The call to the stuff after "something" happens later. "Go" is only ever on the stack once at a time.

Categories

Resources