Everything .NET programmers know about Asynchronous Programming is wrong
hanselminutes.com
hanselminutes.com
We have way over a million lines of c#, in asp.net, mvc and windows forms. We get 80,000,000 http requests a day.
We have no async (delegates or 4.5 stuff), no threading other than what WCF, AppFabric and ASP.Net give us.
About 25% is generic CRUD code but the rest is complicated matching, integration and math code. We also touch most fundamental computer science domains.
This begs the question: you really need async in the language?
Take out all that complicated code, and replace it with a call to an external 3rd party API. They get a hiccup, your response time goes up by 500%, your cpu load drops, and you start timing out connections.
95% is 41ms.
Where the async syntax really shines is dealing with external services. If you're calling a lot of database, web services, sockets etc it can make your life easier.
We do everything synchronously with respect to databases. Integration is all docoupled into workflows.
If you have 80,000,000 machines serving 80,000,000 requests - that's not very impressive. Even if you have 80 servers, that's extremely unimpressive.
If you can do all of that on 1 server, then YOU probably don't need async. In fact, unless you're wasting time, 80,000,000 averages less than 10 requests/second, and peak is likely less than 100 requests/second. That's really not much - I do that with interpreted python.
But every language does need facility for async processing.
edit: it's 1000/second, not 10/second, as everyone down here corrected. Apparently, I cannot math before coffee. (I don't drink coffee). Apologies.
fun mnemonic: the number of seconds in a day is 864e2. (eight-six-four-(e)-two).
Please show your math.
I took 80000000 / (24 * 60 * 60) and I got 925.
So you seem off by about two orders of magnitude.
I took 80,000,000/86400 and I got 926.
So you seem to be off by about 1.
925.9259259
It's on a bell curve. We're regionally tied so we have 80,000,000 requests, 90% of which are over a 5 hour period so we can hit 5000 requests a second on a fun day :)
We do this on 10 servers. 4 front end, 4 back end, 2 database nodes. They are all hefty machines (FE=8xXeon,16Gb;BE=16xXeon,32Gb,DB=48xXeon,96Gb;Storage=12TiB SAN).
Our peak cluster load is about 20% (the sweet spot).
We don't need async, even if we lost half of our nodes.
Or even delegates. Or interfaces. Or LINQ.
Basically you could write the same thing in C.
Or assembly.
The parent isn't arguing that the new async constructs in the language don't make asynchronous programming easier.
He's questioning the need for using asynchronous programming models at all, for the common use case of ASP.NET applications.
And, my comment questions this very idea that for language constructs to be added there has to be a "need".
They is never a hard need. We can do everything with assembly.
Language constructs are added to make things easier.
And that includes ASP.NET applications. His might not have a use for patterns made easier by async, but others do, especially those doing real time websockety stuff.
It is pretty simple for that scenario and avoids the cross thread marshalling you need to do in win forms.
The key question is: while said REST call is happening, what is the caller doing? Normally, he's sitting there and blocking his thread until the call finishes (i.e. consuming one thread from the connection thread pool). With async/await, the thread is released back into the thread pool until the result is ready, then it picks up where it left off.
So anything where you might want to deal with background operations that take a long time (including network / db / filesystem access, i.e. most applications that aren't pure functions) in a connection-oriented system can greatly benefit from async/await. Even if it's just a regular desktop app, it appears to be a useful mechanism for coordinating different background tasks.
The nice thing about async vs. typical closures is that it neatly avoids a lot of nested scopes.
But it's a big leap to go from that to saying that nobody else needs async, so it need not go in the language.
[1] Taking the calculation off the main thread isn't the only way async can help here. The primitives also give you a way for taking better advantage of multiple cores (futures) more easily.
I too have applications that do a lot of stuff relying of asynchronous toolsets but without much explicit asynchronicity. However, I did a simple experiment a couple weeks back - I coded one of our simple but very asynchronous programs in Go. Compared to the Python original (which uses Twisted), the Go version had 70% of the lines and was much more readable and understandable by even team mates who had never seen Go.
I believe that's the point of having async operation elegantly merged into the language.
A better question is how many lines of code the async feature saves, compared to the code you would write without it.
It's about doing more, better, and easier. async allows a pretty complicated process to be much easier to write.
To appreciate async and await, you have to go through the exercise of converting some synchronous code to an event driven model with callbacks. Then do it using async/await. It's more syntactic sugar than anything.
I appreciate it in how I appreciate the dynamic keyword. I know how to dynamically Invoke() methods at runtime using reflection. It's a lot of boilerplate and ceremony, and the dynamic keyword helps alleviate that.
So, the pedantry on this point is pointless and counterproductive.
Just a few weeks ago I was able to convert a nightmare-ish recursive asynchronous method to a `foreach` loop with `await`s inside.
It's cool when you can do this:
var providerExceptions = new List<Exception> ();
// Try each provider in turn
foreach (var pi in providers) {
token.ThrowIfCancellationRequested ();
try {
return await GetSession (provider, isLast, options, token);
} catch (TaskCanceledException) {
throw;
} catch (Exception ex) {
providerExceptions.Add (ex);
// Fall back to next provider
}
}
// Neither provider worked
throw new AggregateException ("Could not obtain session via either provider", providerExceptions);
Or this: async Task<Session> GetSession (AccountProvider provider, bool isLast, LoginOptions options, CancellationToken token)
{
if (!SessionManager.NetworkMonitor.IsNetworkAvailable)
throw new OfflineException ();
var account = await GetAccount (provider, !isLast, options);
if (account == null)
throw new Exception ("The user chose to skip this provider.");
var service = provider.Service;
var session = new Session (service, account);
if (service.SupportsVerification) {
// For services that support verification, do it now
try {
await service.VerifyAsync (account, token);
} catch (TaskCanceledException) {
throw;
} catch (Exception ex) {
throw new InvalidOperationException ("Account verification failed.", ex);
}
}
return session;
}
Depending on the conditions, the method may or may not “freeze”, but the calling code doesn't care.Unless there's more to the method, just remove the async modifier and directly return the task returned by GetSession.
Instead, it unwraps exceptions, and while it propagates cancelations, it ignores other exceptions and tries other providers in turn.
In fact, I could even use `break` or `continue` in the midst of async code, and there would be no problem.
Also I just love using `try` and `finally` in async code—with callbacks, you have to do finalization from all error and success paths (which can be a lot of places).
Second note: If your app has lots of short-ish lived requests, you can just achieve parallelism by having a decent-sized threadpool and just run each request on a thread. Who cares if you have 400 threads? It'll scale well enough (as you noticed).
However, if the app is doing long-running requests/clients, and you dedicate a thread to each one, then you end up with a lot of extra overhead. Using C#'s async model can simplify the code while keeping things lightweight.
This isn't the only way to increase concurrency. Obviously you can grow your IIS web farm as well. But async/await (or some other .NET asynchronous programming pattern[1]) is a one-time .NET development cost as opposed to a recurring infrastructure/hosting cost.
I would love async if it was completely optional and I could call async code from a synchronous context. Like if I could just say `var tmp=await Foo();` within a synchronous method (and it just ran Foo on the current thread and blocked)..
Instead, it requires one of the methods outlined here: http://stackoverflow.com/questions/5095183/how-would-i-run-a...
If you could define a method as async and then call it either syncronously or not the whole point of the async keyword is lost. You would end up with the compiler doing a load of work because every developer will just slap async on every method just in case rather than actually thinking about what needs to be async and what doesn't.
Adding the async keyword is not a magic bullet, there's a huge amount of work going on under the hood every time you use it and it's important that developers appreciate that.
Also, two methods sharing the same code sounds absolutely horrible.
As a result people still use monitors or any kind of blocking wait event. Including the TaskCompletetionSource in the framework I don't think helped this.
A lot of what I've seen on different projects using these newer tools is an attempt to re-create what they would do before. Doing a Producer Consumer, that's a blocking de-queue etc.
How would you implement Observable.LastAsync, without using something equivalent to TaskCompletionSource along the way?
As in “adding TaskCompletionSource to the framework didn't help some folks adjusting to Task style, and they keep using callbacks”.