We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback
00:00

Formal Metadata

Title
C# 5
Title of Series
Number of Parts
150
Author
License
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
C# 5 has one big feature... asynchronous functions, aka "async/await". It's ever so exciting, whether you're writing client code which needs to have a responsive UI without turning your code into spaghetti, or server code where you really don't want to start 100,000 threads just because you've got a high traffic web server which needs to perform long-running tasks. In this talk I'll demonstrate what async functions look like, then dive into how they work under the hood. Be warned: once you've used C# 5, all other asynchronous code will look atrocious.
31
59
Thumbnail
1:00:41
89
Thumbnail
1:00:33
90
Thumbnail
1:00:33
102
Gamma functionExecution unitSoftware testingDisk read-and-write headSimultaneous localization and mappingEmulatorLine (geometry)VacuumCellular automatonProtein foldingInformation managementLimit (category theory)Drum memoryVisual systemSystem callLogarithmBoom (sailing)AuthenticationMessage passingPasswordSynchronizationTask (computing)IRIS-TPoint (geometry)Pattern languageCompilerCodeRight angleContext awarenessComputer configuration1 (number)Multiplication signDirected graphGoodness of fitCovering spaceMiniDiscCartesian coordinate systemAuthenticationUnit testingTask (computing)BitSlide ruleHoaxWeightUniversal product codeProduct (business)Hacker (term)Computer fontCASE <Informatik>XMLUMLComputer animation
PasswordSynchronizationTask (computing)AuthenticationLogarithmSoftware testingDrum memoryDisk read-and-write headSummierbarkeitHill differential equationRankingService (economics)Convex hullThread (computing)Neuroinformatik2 (number)Service (economics)Different (Kate Ryan album)Electronic mailing listMultiplication signParallel portStructural loadMultiplicationAsynchronous Transfer ModeSynchronizationCodeAnalytic continuationWeb serviceRight angleDemosceneWindowMaizeBitContext awarenessBuildingDemo (music)GoogolNumberConcurrency (computer science)Server (computing)Web 2.0Directed graphCartesian coordinate systemClient (computing)Form (programming)WritingPasswordWaveTask (computing)AuthenticationFile formatCodeCompilerComputer configurationMobile appComputer animation
Execution unitScalable Coherent InterfaceArithmetic meanTask (computing)SynchronizationLogarithmPasswordGamma functionDrum memorySoftware testingDisk read-and-write headAuthenticationHill differential equationTotal S.A.DecimalLimit (category theory)Message passingMaxima and minimaShared memoryComputer iconVisual systemToken ringPattern languageRepresentation (politics)Point (geometry)CompilerThread (computing)ResultantWeightEndliche ModelltheorieDemo (music)CASE <Informatik>CodeService (economics)MathematicsTask (computing)BitSynchronizationPasswordSystem callUser interfaceFinite-state machineOperator (mathematics)Order (biology)Real numberCache (computing)Parallel portNormal (geometry)Functional (mathematics)Maxima and minimaImplementationRevision controlDemosceneAnalytic continuationCheat <Computerspiel>Computer programmingDiscrete element methodDirected graphSummierbarkeitRight angleMereologyMultiplication signComputer animation
MIDIAuthenticationSoftware testingDisk read-and-write headLogarithmDrum memoryGamma functionMaxima and minimaLemma (mathematics)Alpha (investment)Smith chartOvalFlagUnicodeTask (computing)Limit (category theory)Constructor (object-oriented programming)Cohen's kappaMenu (computing)Computer iconVacuumEndliche ModelltheorieResultantAuthenticationState of matterSystem callRight angleCodePasswordIterationBlock (periodic table)Amalgam (chemistry)Disk read-and-write headDifferent (Kate Ryan album)Point (geometry)NumberCategory of beingSeries (mathematics)Pointer (computer programming)MultiplicationService (economics)Event horizonSampling (statistics)Finite-state machineWindowCuboidThread (computing)QuicksortMultiplication signBitBlogLevel (video gaming)ExpressionVirtual machineWhiteboardTerm (mathematics)Directed graphStress (mechanics)Mobile appTablet computerQueue (abstract data type)Numeral (linguistics)Closed setMassNeuroinformatikWeightFamilyPole (complex analysis)Compilation albumGoodness of fitOpen setPattern languageReading (process)Structural loadBasis <Mathematik>Uniform boundedness principleComputer animation
Maxima and minimaFlagElectric currentUnicodeVacuumDrum memoryDisk read-and-write headSoftware testingAreaPhysical systemSpacetimeoutputTask (computing)Fluid staticsGeneric programmingSource codeTwin primeCompilation albumSynchronizationSimultaneous localization and mappingOrder (biology)CuboidTask (computing)Different (Kate Ryan album)CodePoint (geometry)Service (economics)Demo (music)Electronic mailing listProgramming paradigmSelectivity (electronic)SequenceComplete metric spaceComputer configurationQuicksortEndliche ModelltheorieBitWeb 2.0Revision controlMereologyParallel portLoop (music)ResultantRight angleLink (knot theory)Goodness of fitSynchronizationPattern languageGoogolChainWebsite1 (number)FeedbackSystem callWeb serviceMultiplication signComputer animation
Task (computing)Total S.A.Software testingDisk read-and-write headVacuumSummierbarkeitSource codeFluid staticsoutputMaxima and minimaPermianTwin primeSimultaneous localization and mappingComplete metric spaceInterior (topology)FlagOvalConvex hullMountain passService-oriented architectureDrum memoryCodeScheduling (computing)Task (computing)Complete metric spaceOrder (biology)Event horizonMultiplication signCuboidResultantSynchronizationSoftware testingSource codeAnalytic continuationThread (computing)Web 2.0MereologyCombinational logicExecution unitVideo gamePoint (geometry)OvalEndliche ModelltheorieDisk read-and-write head1 (number)Programming paradigmSequenceUnit testingQuicksortPattern languageException handlingLambda calculusExpressionPOKEContext awarenessDecision theoryDirected graphClosed setWeb serviceRight angleCodeSystem callFamilyPropagatorExclusive orFreewareGoogolOnline helpGraph coloringCASE <Informatik>Computer animation
Service-oriented architectureOvalTask (computing)SynchronizationDisk read-and-write headDrum memorySoftware testingLimit (category theory)Mountain passSimultaneous localization and mappingGamma functionClique-widthDampingException handlingService (economics)Information managementVacuumSet (mathematics)PasswordString (computer science)AuthenticationParameter (computer programming)Parallel portTotal S.A.ImplementationSerial portUltimatum gameAsynchronous Transfer ModeLoginMetreBroadcast programmingMaxima and minimaConvex hull2 (number)Time travelMultiplication signCodeTask (computing)WritingService (economics)Product (business)Execution unitAuthenticationInjektivitätSystem callRevision controlPoint (geometry)BitSoftware testingTotal S.A.CalculationComputer programmingCASE <Informatik>Order (biology)PrototypeResultantDifferent (Kate Ryan album)Right angleDimensional analysisInterface (computing)Flow separationGoodness of fitPasswordUnit testingThread (computing)Universal product codeOvalLatent heatException handlingDemo (music)MereologyView (database)TouchscreenDirected graphSynchronizationECosImplementationWeightEvent horizonMessage passingSerial portGradientLattice (order)Computer animation
VacuumSoftware testingSimultaneous localization and mappingService (economics)Mountain passGreatest elementVisual systemSimulated annealingDisk read-and-write headGamma functionPermianPredictabilityConstructor (object-oriented programming)Wechselseitige InformationLocal GroupImplementationSerial portPasswordString (computer science)SynchronizationExecution unitTotal S.A.Limit (category theory)Maxima and minimaHecke operatorState of matterCodeSoftware testingThread (computing)Bit rateTask (computing)Link (knot theory)Equaliser (mathematics)AuthenticationObject (grammar)Loop (music)Complex (psychology)WindowSystem callAnalytic continuationException handlingRight angleMultiplication signOrder (biology)Concurrency (computer science)MultiplicationGroup actionComplete metric spaceSource codeExpressionCondition numberSynchronizationFinite-state machineUnit testingHydraulic jumpWeb serviceDefault (computer science)Range (statistics)MereologyGraph coloringParameter (computer programming)Point (geometry)String (computer science)BitPattern languageGrand Unified TheoryResultantEndliche ModelltheorieComputer architectureView (database)CASE <Informatik>Poisson-KlammerLambda calculusCasting (performing arts)DeadlockOvalBlock (periodic table)IterationFlow separationThermodynamic equilibriumDecimalComputer animation
XMLUML
Transcript: English(auto-generated)
Do come and sit, kneel, whatever. There's plenty of actual sitting room on stairs and things. I'm sure there won't be a fire.
Having asked you, I missed how many of you were here two years ago. Right, not very many. That's good. How many of you were here last year? More. Did you go to Lucian Visek's talk last year on C-sharp 5 and Async? No? Very few. Good.
So, my problem was, I came here two years ago and did two sessions on C-sharp 5. And so this year, the committee said, well, what would you like to speak about? And I said, well, I could speak about these things. And they said, ah, C-sharp 5, we'll have some of that. And I'm thinking, I don't know what to do. Oh, more coffee. Right, this is going to be really good.
You think I'm enthusiastic without caffeine. So, hands up, those of you who have used C-sharp 5 in production code and are comfortable with Async and Await. Good? No, seriously good, because otherwise, if at some point in this preamble,
you think, hmm, okay, it turns out this isn't going to be useful, then please leave and I promise I will not take offense. I will take offense if you say later on, this wasn't useful and I realized that it wasn't going to be useful at the time that you said you could leave,
but I felt embarrassed, okay? There's no excuse. We can get everyone else to close their eyes. It's fine, leave. So, no one's using it in production yet. That includes me, because I'm only an amateur C-sharp hacker. I just do this for fun, for a hobby. How many of you have played around with Async and Await
and have written some code in it? Quite a few, okay, that's going to pose a bit of a problem. How many of you have read a bit about it, but not actually really looked at it in any kind of detail? Okay, good, and anyone just have no idea what's in C-sharp 5,
but hey, this sounded fun. Okay, yeah, it's a short session title, there's not much to comprehend there. Okay, so that leaves me at a case where no really experienced people, so I can say what I like and no one's going to contradict. That's great, I will try to be accurate.
But it means that it's difficult to know exactly how detailed to go. I have enough material, I have no slides, but I have oodles of code. If I run out of code that's in this particular solution, I may need to fiddle around on the disk or just write some more code. But I'm sure we can find enough interesting code to cover hours and hours and hours.
But the question is, what would be the most useful to you? So it sounds like most of you have seen something about Async. If I produce an application, which you really don't want me writing applications in general, but this is okay, it's toy.
If I show you code that looks like this, we get a task. Let me turn up the font here after all, because I've just remembered it. It's bigger than it normally is, but it's only big enough for the workshop for me to rant about date time. That's the other thing we could do if we've got time. We could just rant about date time for as long as we like.
So if I show you code that is an Async method, and we get a task of nullable grid from some method, authenticateUserAsync. I haven't given you nearly enough context here, but you've got a method that returns a task. And then I do await.
How many people are going, this is syntax I have never seen, I really don't know what's going on here at all? Only a few. Are you good at catching up? Excellent. Right. Because there's no point in scurrying around for other people. Boring other people doing stuff they've already seen.
So what we can look at today, I will give you some options. There's how to unit test Asynchrony. And when I say how to, I mean one idea of how you might like to. It's far from the only way you could do it. We could look at what the compiler does for you under the hood.
We could look at the patterns that are produced or consumed by the compiler. And if any of you, how many of you were in my abusing C Sharp code talk yesterday? Seems a long time ago. Okay. Some. We could look at some of the details of how that hooks together.
We can look at, I will almost certainly do my magic trick, because I have some magic code that's absolutely fabulous. So, out of those things, how many people are really interested in the unit testing side of things? Oh, about half. It's going to be about half for everything, isn't it? How many are interested in the code that the compiler builds behind the scenes?
Oh, less than half. Okay. Right. So let's start with a little bit of background, just to get you back up to speed. So, writing asynchronous code is hard without any tooling,
because you get into a messy maze of twisted little continuations all the same. Asynchrony is not about threading. Asynchrony is about a mode of execution where, in synchronous code, I say, I want to do something. I will wait until that has finished.
That has finished. I will now go and do something else. Asynchronous execution is, I would like a pizza, please. I will do some more coding. Pizza's arrived. Excellent. I shall eat pizza. So it's about starting something off and then reacting when it has finished.
That's all it is. It can all be on the same thread. It could be on multiple threads. There could be one thread locally, but you're making a web service request, and something tells you on your one thread when it's finished. So please don't think asynchrony equals parallelism.
Equally, don't think that because I'm demoing things, actually, yeah, okay, one other option for us to look at. I have here an application. I'll demo the application in a minute. A Windows Forms application that uses async that then talks to a server that is also implemented with async.
So how many people have seen async on the server side of things? Oh, relatively few. You know why that is? It's because it's really easy to demo a client's app doing async, because everyone has written a Windows format and tried to do something long-running, and you've got, oh, look, I can wave the mouse, and the window isn't moving. It's really easy to demo that.
What's slightly harder to demo is, I have 1,000 tasks going into this server, and they're only using one thread. All you can do is, right, okay, I'm looking at perfmon, and it looks okay. Really boring. But if you're writing server-side code,
do not think that asynchrony is all on the client side. When I said asynchrony isn't about threads, it's almost about removing threads, because when you want to do lots of things at the same time, when you have concurrency, that doesn't necessarily mean that you need parallelism,
multiple threads of execution. If you can offload a load of those things to somewhere else and then react when they've finished, you can have lots of concurrency, very little parallelism. If you have 1,000 requests coming into your web server, all trying to get stock market quotes, and you need to hit one web service to get authentication data,
another to get, well, okay, I'm now talking about the exact example I've got here. You need to authenticate the user. You need to find out which stocks they hold, and then you need to go to possibly various different stock price services. When you've got all of that, you can return to the user saying,
here is your stock portfolio. How much of the time do you need to be spending doing work? How much actual work is there in saying, ah, I've got a password. Send that off. Oh, I've got some stock lists here. Right, I shall send requests off,
and then I shall gather them together and add up. Computers are pretty fast at adding up. If it takes a whole thread for the whole one second or two seconds for a really slow web service to come back with stock prices, and all you've done, all the value you've added is adding numbers,
you're being very wasteful. Asynchrony on the server side allows you to get away with huge concurrency with very, very few threads. Let's show the demo app just for the sake of getting some kind of context.
This is exactly what I said before. We have three services, an authentication service, a stock portfolio service, which comes back. You say, I am user Fred. I'm authenticated user Fred. What stocks do I have? You say, I've got three stocks of Apple, and five of Microsoft, and ten of Google, whatever it is.
Then I've got another service, which is given a stock ticker, what is the price? Here I get to do the I'm demoing client-side stuff, so I can whiz things around. That always happens. I always forget that if you whiz stuff around in Windows 8,
it makes whatever's behind it go away. You can see that stuff is happening. I've got a log. It's too small to really see from the back. I've got a log of what's going on, and things are happening. I only have a single thread in this code. In fact, it's getting all the data locally, but it's pretending to go off to a service.
Let's have a little look at what is happening behind the scenes and how we should use this idea of I have started something, and I want to come back. The code here is pretty dumb to start with.
I think I've got the dumb version. Wow, this is older than I thought. It's okay. No, this is a mercurial thing, and yes, I've got demo one. This is less efficient than it could be,
but it's relatively easy to think about. I want to take a step back. Because most of us have seen async code, the actual code here shouldn't be much of a surprise to you, but I want us to take a step back and think about not just the execution flow, but the whole execution model.
We come in, and we are on the UI thread, and we've just been called because the Get Stocks button has been clicked. We start doing stuff. We don't start a new thread because asynchrony isn't about threading. I'm going to keep hammering this. I really am.
What do we need to do? Let's forget the actual code. That's a representation of what we need to do. We need to authenticate, find out, assuming that they managed to authenticate, they've given the right password. We need to find their stock portfolios, then we need to find the price of each of those stocks and add them all together.
That is fundamentally any code that implements this user interface is going to have that pattern. That's not the execution model. That's what we're trying to describe in a particular execution model. We could do that in a synchronous execution model,
and we know what would happen. The user would have to wait before they saw anything, and if we did it all in the UI thread, they wouldn't be able to use the user interface to start with. But if we look at the code, we can see that for the most part, it looks like the synchronous execution model with a bit of a change.
And the bit of a change is that whenever we're asking for something that isn't going to come back straight away or might not come back straight away, it's not even guaranteed that this will take a long time, maybe we've got a cache behind all of this. We don't get the answer.
We are used to asking questions and doing nothing until we've got an answer. That is our normal execution model when we write programs. And actually, the real world isn't like that at all. I don't put an Amazon order in
and then sit by the door until it arrives. I get on with things. Until I'm blocked because, you know, OK, I'm going to start doing C Sharp on Xamarin.iOS for a bit of fun. So I haven't ordered it yet, but I will order a little Mac Mini to help with this.
And there's nothing else I can do on that particular task until the Mac Mini arrives. So sometimes you do need to kind of wait. And that's what the await thing here is about. But we need to separate out starting some asynchronous operation
from waiting for it to finish. There are far too many demos I've seen where the code looks await something async, await something async, as if that's what it must look like that await comes with a method call that returns a task of T.
Await is not that. And the idea that you can't do the call something that returns me a task of T without awaiting it is also wrong. So our execution model isn't about await something
or rather await the results of this operation and then I can keep going through the next operation. It is start something and give me back a token. Now the token we tend to use in .NET is task of T, or task. And it acts as, I like to think of it as a cardboard box.
So we have a cardboard box that has a promise on it. So you'll hear this written as future and promise. I think in fact the guys who invented the TPL are now wishing that they called it future or promise instead of task of T, because task sounds like something active.
And in a lot of cases it won't be active locally at all. It's a promise that at some point something will give you back an answer. Or it may never come back, but you know, it's the promise of something in the future. So that's one separable concept. Even without C sharp five we have the idea of
give me something that is a token representing a future result. And we can now think what does it mean to have that token? Okay, we shouldn't just look at task of T
because that's not the only model for this. But what is it that we need for asynchrony to work? So we want to be able to express the idea of okay, at some point I really need the result from this token. I need to cash in this IOU
because I can't do anything else yet. But I want to do that without blocking, without blocking my current thread. Asynchrony isn't about threading, but I'm going to keep on talking about not blocking the current thread. It's unfortunate that the two are linked, but it's not about parallelism.
If you'd like to think of it this way, asynchrony isn't about making lots of threads do something, it's about making no threads do something. So we move from the synchronous model, which is while I'm waiting there is one thing, to one thread running, to while I'm waiting there are no threads,
no threads that I care about. So, question for you, and I'm going to wait until I get an answer. What do we need from some token, some promise? What's the minimal functionality in order to make asynchrony work?
Yes? Get a waiter. Get a waiter. No, you've cheated. You're going straight for the C-sharp implementation. What do we need to be able to do with that for asynchrony to work? Get something waitable. We're still tied down to the implementation.
What we need to be able to say is, I'm going away now. Tell me when you're done. Callbacks. Yes. Continuations. That's all that's really required. And the .NET implementation of that is the awaitable pattern,
which adds a couple of extra things on top that aren't strictly required, really, but allow everything to be a lot smoother. So, I'm not going to show you the exact state machine that the C-sharp compiler builds because it is a full state machine. How many of you have decompiled an iterator block?
Yeah, I never get much reaction for that. Iterator blocks introduced in C-sharp 2, and they built state machines as well. So, if I have a method that returns IEnumerable of foo and I do yield something, yield something else, yield something else,
it builds a state machine. When I call getEnumerator and then move next, it says, ah, I shall run some of your code. Right. Oh, I've got a result now. I need to return that. I need to remember the state of this method. Fine, here's your result. And then, when I next call moveNext, it says, oh, right, okay, I was in this state.
I shall keep going. Oh, I've got another result for you. Okay, I'll remember my state. Fine, there we go. And I'll do something with the result. And moveNext and, you know, I could keep running around until 20 past five, but let's not. So, we have the state machine in C-sharp 2.
C-sharp 5 builds another state machine where the model is kind of different. We start the state machine, and the state machine keeps running until it can't go any further. So, sometimes our promise, our cardboard box that says at some point I will have a value in it,
sometimes the cardboard box is already open by the time we get it and we can see the value. So, the state machine says, right, that's fine, I will keep going. And then it says, oh, no, there's a cardboard box that's empty. I need to wait. Tell you what, okay, I'm not going to stand here waiting all day. Me as the thread. I shall return to you having put something on a cardboard box
saying, give me a call when you put a value in here. So, the code that is generated in the state machine, when we hit this await expression, when we hit the calling the authenticate user async,
that just calls a method. There's nothing magical about that. Whether or not that method is implemented using an async method is irrelevant. It's just a method call. We would hope it will return quickly. Everything that you do in an async method should return quickly.
Even if you won't get the result quickly, it should return quickly. And this is the sort of thing, there's only so much I can do for you in terms of rewiring your head when it comes to the execution model of async. I will try to bounce around on stage and hopefully something that I haven't planned out at all,
which is like everything, but something that I haven't planned out at all will light a light bulb in your head and you'll say, that's what he was talking about. Maybe it'll be when you're reading some other blog later on and suddenly it'll make sense. Don't expect that you can watch one talk or read one blog or read one book
and get async. Maybe it'll happen, but my experience is that people hear different viewpoints, different ways of explaining the same thing. They're all probably right, but suddenly it's the amalgamation of them that works. So I hope that something I do either clicks with something you've already seen or something that you will see in the future.
Where was I? When we hit the await expression, that's when we get into our awaitable pattern, which is more than it needs to be, but it says, given this awaitable, I need some tool that I can use to await it. Again, this isn't strictly necessary,
but it makes things a lot simpler. You have to be able to ask your promise for an awaiter and then you have to be able to do three things on the awaiter. You have to be able to say, have you already finished?
Is my cardboard box open? I'm going to keep using this cardboard box metaphor because it'll come really handy later on. Is the cardboard box already open? Why do we need to do that? Because if it already is open, then it's pointless me sticking a phone number on the front and saying, call me when you've put something in it because I'm there, I can see it now.
I can be much more efficient by just keeping going. Okay, so that's the first thing. That's the isCompleted property. The details of all of this, it's all pattern-based, it's all funky. There are multiple blog posts about it, by me, by Eric Lippert, by Mads, all kinds of things.
Just to advertise, there's a TechPub series that I've done about async. There's C-Sharp In-Depth 3rd Edition will be out soon. The details can come another way, and it doesn't need to be anything I've done. There are loads of things about it. But there's the isCompleted property to say, is the cardboard box already open with the results showing?
Fine. There's the onCompleted method. I'm going to check this now because it's been a little while. I think it's onCompleted, under the hood. Logicals, decompilation sample.
This is an example of the state machine that's been built, and I'm really just checking that I'm using the right name. Get awaited, yes, isCompleted, yes. There's onCompleted and there's awaitUnsafeCompleted. I think it may be awaitUncompleted these days.
Things have changed a bit over time. I need to be able to say to the waiter, effectively, the code goes through several hoops these days. It's much harder to understand, but much more efficient. You never need to look at this, other than for fun.
You need to be able to say, call me back when you've put something in the box. That's sort of writing your phone number on the front of the box. That's the second thing. Is the box open? Write your phone number on the front of it and now I know the box is open, get me the result. Those are the three things.
All of those could be put into one, which is call me back with the result when it's there. Then it could just call you straight back if it's already there, if the cardboard box is already open, and give you the result up front. It's just simpler to separate the three.
So that is the basics of the asynchronous model. I'm going to ask for authentication. So I say to the authentication service, give me a cardboard box that will be the promise of a result
that the user is John, the password is Fred. And they immediately, or really quickly anyway, give me back a cardboard box saying, yep, at some point this will have your result in. I look at it and say, it's closed. Okay, here's my phone number. I shall go away, I shall do event handles. Mouse pointer's moving, mouse pointer, mouse pointer.
Oh, right, authentication services come back. They give me a call saying, your parcel is here. And yes, this is user number five. I say, right, okay, user number five, that's good. Okay, I'm on the UI thread, I've got to be quick, got to be quick. Stock portfolio service, give me a cardboard box for user number five.
Give me their portfolio. Right, fine, okay, that's closed as well. Okay, and mouse pointer moves, and let's wipe out whatever was behind the app and stuff. And then the portfolio comes back. Okay, I'm not going to keep laboring this anymore. Are you all reasonably happy with that's the execution model? Are you reasonably happy with the difference between that and the blocking model?
Where I go, authentication service, I give them a call, and they say, your call is very important to us. You are number 53 in the queue, I say. It's okay, but there's a mouse moving around there. Yeah, yeah, okay, okay, right, okay.
What's happened? What, what, what? The window closed? Ah! So, okay, those are the two different models. Let's show you the magic with the cardboard boxes. How long have I got? 35 minutes. Okay, I want to show you one of the other nice things about the asynchronous model in C Sharp 5.
Oh, remind me when I've got five minutes left, because I need to show you the features of C Sharp 5 that aren't async. I think I promised that. Do any of you care? No, okay, maybe I won't then, fine. Right, let's do, oh, it's not in this one, it's in this one.
Okay, yes, reload. I'm really hoping that somewhere in here I've got magic ordering. Yes, okay. So, let me give you the background to this and the reason why it's an important demo.
I'm going to show you a method that sounds magical and doesn't use an asynchronous method at all. You can do this in C Sharp 4. It's just it's less useful. We have our portfolio, I'm going to keep the same example,
so they've come back saying, you've got these 10 different stock tickers, fine. And I can say, okay, I now want to send off all the requests in parallel to maybe different web service, maybe the same web service, to say, get me the stock price for Apple, get me the stock price for Microsoft, get me the stock price for Google. I always use those three examples.
And then I'm going to say, right, I've phoned them all up, they've given me these cardboard boxes, I will go away again. When do I want to give the user any feedback? I have three cardboard boxes.
So, I heard someone say when they've all been opened and have results. And I heard someone saying when you've got two of them open. So, okay, so when the first of the prices is...
Right, okay, so where the first box is, the portfolio itself. Right, so I've got three boxes that are going to have prices in. And the thing is, when the first of them that has a price returns, as it were, when it's filled, then I would like to give some feedback.
Because otherwise, if I wait for everything, the user's going, this is taking a very long time, and then something goes, blah, there are all your results. Well, sometimes that's absolutely the right thing to do, but often it's not. So, I'll assume we can launch things in parallel.
Those of you who have done, stick your hands up again if you've done some async await. Okay, you know how you can launch things in parallel, because you can call and not bother awaiting just yet. How do you do what we want to do, which is when the first result comes back?
When any? Yes, you can. But then, so you can await task.whenAny, which is a method that takes a sequence of tasks, and when any of them return, it will tell you which task has completed. And then I can update the UI at that point.
What am I going to do then? I'm going to call it again, but now I need to call it without that completed task, because otherwise it's going to just return again. So then I've got to manage all this stuff, thinking about, I'm trying to write code that is simple to understand
and looks a bit like the dumb synchronous version that reads top to bottom, because we know how to deal with that. There's another option that we haven't explored, which I hope you'll agree is obviously not what we want to do, but it does sort of work.
So I call Apple, or rather, I call the price service and say, what is Apple's price? And I get a cardboard box. I then call, what is Google's price? I'm doing this in alphabetical order just for simplicity. What is Google's price? Cardboard box. What is Microsoft's price? Cardboard box.
So I have three cardboard boxes in front of me. I can, rather than say, right, I'm not going to do anything until all of them are done, or I'm going to take the first one to complete and then somehow kick it off the stage and wait for the rest and then kick that one. I could just say, do you know what? I'll just ignore those two. I will wait for the price for Apple to come back.
It may be the first one to come back. That's fine. And then I can await the second one. And then I can await the third one. That's really easy code. Let me show you what that code looks like.
So fetch prices, parallel, async. Okay, well, if I just get rid of the magic bit, here are a bunch of tasks. So I have literally just asked each holding, so each ticker that I've got, right, go and get me the task
that will find the price of that ticker. And I've created a list of those tasks. Those tasks are hot, as it were. They're going. And then I'm going to loop over all the tasks, await the task, find out the price, add it to my UI,
and update my total net worth. Would you agree that's easy code to understand? Yeah? Probably easier than using when any and then get rid of the one that was already there. The only thing is, it comes back in the wrong order. What we would love is to have this for each loop
and say, don't just go in the order in which they came back, but instead, wait for the first of them. Okay, so we've got Apple, Google, Microsoft. Which one do we want to return first?
Apple? Okay. No, as in, which one would you like to make me wait for? Google. Google, okay. So this one is going to return first. Ideally, what I want to do is have this loop and happen to be looking for this. I'm ignoring those two, ignoring those two, wait for you to finish, yay.
And then which one's going to come next? Maybe Microsoft. Wait for you to finish, wait for you to finish, yay. Done. And then Apple. We clearly can't do that because we can't know which one to await, right? Oh, yes, we can, sort of, with a bit of magic. So the magic is, in completion order.
This isn't part of the framework, although if you read the task-based asynchronous pattern, or task-based asynchrony pattern, one of the two, a white paper by Stephen Toob, awesomely written,
it has this in completion order, but called interleaved for no particularly good reason. Brilliant paper, rubbish name. This in completion order is not an asynchronous method, but it demonstrates the compositional aspect of asynchrony. Why do we love link?
Because I can separate where from select, from select many, from all of these things that all work with a sequence. Why do we love async? Because we can have one async method that calls another async method, that calls another async method, and we just await up the chain, and everything works nicely. Stay within the paradigm.
You will find when you're writing asynchronous code that your nastiest points are where you need to switch between asynchronous and synchronous code, either way around. So switching from, if I need to call some synchronous code, and I'm within an asynchronous method,
so I want to call something that might take a long time, I need to decide, okay, I'll use task.run or something like that, and that will fire up a thread potentially, or use a thread from the thread pool. It's not great, but it can be useful. And if I'm calling from synchronous code to asynchronous code,
it's going to return me a task of t. And then I need to think, well, what do I do with that task of t? I'm synchronous code. I can't schedule a callback. I'm synchronous code. I can block, I can wait for you to finish, at which point I lose all the benefits of you being asynchronous in the first place. How you handle that is entirely context-dependent.
I can't give you any solutions. But I'm just saying, these switching points are your pain points. Stick within the paradigm. And within the paradigm of asynchrony, when we've got a sequence of tasks
and we want to know how they finish in the completion order, we should give you back a sequence of tasks. So the magic of incompletion order is you give me, me being incompletion order and you being the caller, you give me three cardboard boxes.
Apple, Microsoft, sorry, Apple, Google, Microsoft. I give you three cardboard boxes that don't have labels on them. So you give me three cardboard boxes, I'll watch over them over here. And I give you back three cardboard boxes. Great. And then all this method does is say,
these cardboard boxes, you can't see, but they've got a hole in the back. Tell you what, I will register a callback with each of these cardboard boxes and I'll keep a little counter to see how many of my new cardboard boxes have already been used. So we start off saying, okay, we haven't used any of the cardboard boxes,
I've given you three empty closed cardboard boxes. I've got a hole in the back, you can't see it. This is task completion source of tea, okay? For those following at home, as it were. So I've got these boxes that you gave me, empty boxes with holes in the back, and I will say, all of you,
when any of you fires, give me your result and I will take that result and the fact that I have the counter, I know how many of these boxes I've already filled, and I'll just fill that cardboard box. So maybe it was that one that finished first, doesn't matter, maybe it was that one. Either way, I'm gonna fill this.
And then when the next one completes, I will fill this. I don't need to use when any because I'm registering callbacks. So I give you tasks back which are guaranteed to complete in the order in which I give you them. Which means you can just await one at a time
in that order. And as soon as the first result comes back, whatever it is, I'll poke the hole in there, your await will complete, you update the UI, life goes on. And the code is as simple as for each, as I've got it up there.
And this incompletion order is not difficult code. It's inspired code. I didn't create it, so I can say that. I didn't have the idea. But it's not hard. We create these cardboard boxes,
task completion sources, and then we remember because we've got lambda expressions which can capture current index, and then we just need to use continueWith within propagateResult. So propagateResult is just a little helper method to say shove the result of this task
into this cardboard box, and we're done. But the thing I'm trying to get across is this is not part of C-sharp 5. This is a combinator. How many of you have been to functional talks in the last few days? Yeah.
Did they talk about combinators? Yeah. Don't think about combinators only applying to collections. Just because we happen to see them with collections a lot, this is a combinator. There are a bunch of other ones. When any is a combinator, it takes a bunch of tasks and returns you a task. There are others to explore.
So where have we got to? We've looked at how the asynchronous execution model is different to the synchronous model. We've noted that if we stay within the model of asynchrony, of promises, this only ever looks at a value when we know we've got it,
and it sticks within that asynchronous model. Life will be a lot easier. And we're looking for patterns that let us write simple code. If we use task.whenAny, it would have been absolutely fine. I have a sneaking suspicion I said innumerable.whenAny earlier on, which would be very, very confusing.
I mean task.whenAny. You can use that sort of thing. But as soon as you're manually registering callbacks within async methods, something is wrong. It may be that actually you've got an ugly situation and what you're doing is the cleanest way of solving that ugly situation.
But in general, the way that you attach continuations within an async method is to use await. That's what it's there for. That's all it's there for. Where have I got to? Who said they want to see unit testing?
Quite a few. There are various ideas about unit testing for asynchrony. First things to know, MSTest and NUnit and XUnit all now support asynchronous unit tests, which means you can have
normal code that can be written as badly as you like, really, that will call out web services, whatever it is, and you can write tests that are async. When you do, the key thing to remember is you return task from an async method that is a test.
In fact, this is generally a good idea on all asynchronous methods. Apologies again if you hadn't used async before. This may be going, wow, miles over your head. An asynchronous method can only return void or task or task of t.
When you return void, you say sorry at the same time because you don't want to be doing that. I can't emphasize this enough. The only reason why you're allowed to return void is so that you can wire up an event handler with an asynchronous method.
No other time should you just return void. It's a waste. When you return task, think of that as returning task of void. If we had the idea of unit from FSharp, then we wouldn't need task. We would only have task of t and we could do task of unit or task of void
because that is logically what you're saying. An asynchronous method returns a promise saying I will let you know when I have finished successfully or not. You can see an exception or you can see that I've completed successfully. If you just return void, that's like calling Amazon
and saying I would like to order something and they just put the phone down on you. Maybe your order will turn up. Maybe it won't. You have no way of asking them, hey, where's my order? What order? I don't know. Always, always, always return task. There is another reason within unit tests particularly
to return task when you're writing an asynchronous test. If I write with nUnit 2.6.1, I think, a method like this, it will be valid. It will compile.
It will run. Your tests will pass, even if your code is broken. Why? Because nUnit 2.6.1, I think, didn't support asynchronous tests. But why did it run? We have an async method. Yeah, what's an async method? It's a method that's got the async keyword at the start.
Yep, how is nUnit meant to know about that? It doesn't support async things. As far as it sees, that's just a method returning void. And it knows how to handle methods returning void. If you use task, and if you try to run that on a version of nUnit that doesn't support asynchronous methods,
it will go, that's not a test, and all will be well. Because you'll say, oh, right, okay, I need nUnit 2.6.2. Or whatever it turns out to be. Okay, so you can write unit tests that just do this, and you can return a delayed task.
So it's really easy to write a method to return a delayed task. I'm assuming that we're all happy that dependency injection is a good thing and separation of concerns and isolation. So I'm going to mock out my authentication service or portfolio service, whatever.
So I'm going to implement some interface that returns a task of T. So where previously I'd have said, I want to test what happens when it throws an exception. Instead, you say, I want to mock the situation where it returns a task that is faulted. Or, and this is where it's tricky,
I want to simulate it returning a task that doesn't return for a while, where a while is however long you want it to be. If you've got multiple tasks, I want to simulate this task completing before this task. What happens? Does my code still do the right thing? Okay, so it's an extra dimension of testing,
and it's easy to write code that does this using the normal definition of time. Okay, this is a little bit like, if I'm writing some code that says after the user has clicked on add comment, they can't click on add comment again for another 15 seconds.
So what will I do? I will simulate adding a comment. Then I will do thread.sleep for 10,000 milliseconds, and I shall try to add a comment and see whether that works. No, it didn't. Good. I shall then sleep for another five seconds, and then see whether it works. Oh yes, it does, because it was 15 seconds since the first comment.
We wouldn't do that because we don't like unit tests that take 15 seconds. We don't like unit tests that take any significant amount of time at all. What we need is a time machine, which is exactly what we can do. So you can write unit tests that just use task.delay,
and that will wait for long enough, and then will return. But I think we can do better. I think we can programmatically say exactly how we want things to work. So I have a time machine class.
I'm not going to suggest that this is production ready. I'm really hoping, don't forget C sharp amateur here, I don't write any code that uses async, but I don't write production code at all. So I'm hoping that one of you guys will say, do you know what? John's right, this is a good idea.
Let's take John's prototype and write some good code from it. Because I think this is a way that we should be testing asynchronous things. It won't always be suitable, but in some cases it will. So when we're in the situation where we control all the tasks that our code is going to depend on, so let me just show you the code that this is a test for.
I'm going to call calculate worth async, and I'm going to validate arguments, remind me to pop the stack and come back to this point in five minutes. And that's going to call calculate worth async impl. So what we want to be able to test is we're going to authenticate the user
and we shouldn't do anything else if that failed. Otherwise we should get the portfolio, await that returning, and then, sorry, I've got parallel and serial versions just to prove that it doesn't matter.
I can run the same tests with different implementations, and the end result is the same, so I'm happy. So I can look up the price and wait for everything to happen and return the right total at the end. And my tests need to describe all the possibilities.
So what are the possibilities here? We could fail to authenticate. We could authenticate and then get the stock portfolio and ask for all the prices, and the third one comes back before the second one. And the first one is faulted, so that failed. What's the end result?
And when we go to the authentication part, sorry, when I'm in the N unit test, not the MS test, I happen to have demoed this. So all of this code was from the screencast that I did with Rob Connery. So I've got N unit tests that use this time machine
and MS tests that don't, but it could easily be the other way around. There's nothing N unit specific here. So if I have a single item, let's look at this. So expect the code for your async tests to be a bit longer than they would otherwise,
but there are nice things happening here. Note that this is not an async test. This is asynchronous test of asynchronous code, and it's all on one thread. So we're going to start by creating a time machine, work out what our user's grid is and what their stock portfolio looks like,
and then I'm going to ask the time machine to schedule a task. So I'm in my own little world of time here where time just goes one, two, three, four, five. Nice and simple. And I'm going to say, give me back a task which we'll complete at time one with a result of grid.
Also give me back a task that we'll complete at time two with the portfolio, and then give me a task that we'll complete at time three with this price. And then I will use... Yeah, this is RhinoMocks. All I'm doing, I'm programming my mocks
as stubs in this particular case, but it's much of a muchness, to say, you should expect to get a call asking for the authentication for user John with password pass, and you should return that task that will return at time one.
And then I want you to expect that the portfolio service gets et cetera, et cetera. All happy so far? So I'm setting up, I'm arranging. It is that way around, isn't it? Yeah, I'm not dead keen on this triple-ating, but we'll see where we go. This is the interesting bit.
So having arranged what this mock is going to return, I'm then going to call my async method. And then I'm going to control time. So I call my async method, and it gives me back a task of decimal. It's an asynchronous method. It's got to do that quickly.
My tasks won't have completed. By the time this completes, it will have asked for authentication details, but it can't have asked for the portfolio because we're not at time one yet. So it can't have got back the user's grid. So I'm asserting, and this is a fairly detailed test, but you would have a range of tests.
So, so far, we're still running. The state machine hasn't finished. Then I'm going to say to the TARDIS, make time go forward, please. And so by default, we're at time zero. So we'll step forward to time one.
What happens at time one? The task completion source for the authentication task finishes. So now our state machine jumps back into action and says, yes, okay, you've managed to authenticate. So therefore, I will ask for the next thing, and that's not completed yet. So, okay, I will return.
Where does it return to? It returns to our test. So advance basically pumps everything until we've reached some kind of equilibrium where the state machine is waiting for more things to happen. And then I can say, right, by now, I still shouldn't have a result.
Let's advance time a bit further. Oh, the portfolio's come back. We need to do stuff. And obviously, you could test other things at this point. This is a relatively simple case. But imagine that your code here, when it got that authentication was successful,
poked a bit of your UI or your view model or whatever it is, whatever architecture design you're using, to say, I've managed to authenticate successfully. You could test that before the whole thing had finished, but when you've got the authentication bit, it's already done the right thing there.
That kind of timing is really hard to do unless you can control time, unless you have the hand of the clock and can say, move forward one tick. And I keep going forward here, and we assert that the task has completed
with a result of 10 times 34 and a quarter. OK, no threads, no race conditions, no possible deadlocks. There is nothing... This is a completely stable test. This is what I like. This is just one example
of how you can do unit testing with Asynchrony. Look at what other people say as well, but the main thing is, don't think. Typically, in the past, when you have written parallel code, how easy has it been to test that code? Not fun, is it? Does this look easy?
No, it's definitely not. Compared with the synchronous code, there is more to think about here. Asynchrony is hard. You will need to think. Does that mean C-sharp 5 doesn't buy you anything? Absolutely not.
This big ball of complexity has two parts to it. There is the inherent complexity of concurrency. Multiple things are happening at the same time. They may not be threads, but we've got multiple things going on here. We've got potentially many state machines interacting. We have inherent complexity,
and we have incidental complexity, which in the past has been me saying, call me back to this lambda expression. Oh no, now it needs to be a separate method, and I've got to create some state over here, and I've captured a variable, and what the heck do I do if there's an error? And I need to loop. How do I loop with a continuation?
It's a nightmare. This ball of incidental complexity mostly goes out of the window. That is what C-sharp 5 buys you. It is not a panacea. It lets you focus on the difficult bits. And they are difficult. And you should expect your tests. If you're trying to consider everything that could possibly happen
in a situation with lots of tasks, maybe some have been faulted, maybe some have been canceled, maybe they've come in in the opposite order to what you wanted, it will be tricky. But this is controllable. I can get a handle on the complexity and tame it.
Yeah? Excellent. Right, so probably the final bit at least before questions, and we'll see whether we have time for other things than questions. I hope some of this has been useful, by the way. I really did show up saying, I can talk about C-sharp 5 in many different ways. Let's see what sticks, as it were.
So, one point about exceptions. If you throw an exception from an async method, when do you think that exception shows itself? When it's awaited, yes. So, at any point, even if it's the very, very first thing in the method,
if you throw an exception, that will get captured in the task that is returned to the caller. Because you are returning a task, aren't you? Yes, you wouldn't return a void method, no. Okay? So, the caller gets to see that something went wrong, but only when they await it. That's absolutely what you want for the kind of exception
that says the web service was down. Because you probably can't detect that beforehand, and certainly it's not the caller's fault that the web service is down. However, when the caller has been dumb, you want to shoot them as soon as you possibly can, okay? For the kind of exceptions that they should not be catching,
because if they were catching them, they should know how to avoid them in the first place, you really want the exception to be thrown as quickly as possible. So, argument null exception is the typical example here. So, here we have a method that's returning a task of t, and I'm just using a normal if,
and we're going to throw an argument null exception. So when's that exception going to be thrown? Immediately, before await. Why? It was a trick question. This isn't an async method. This is a pattern you need to know about, assuming you agree with me.
Some people disagree and say, just let any exception happen lazily. That's fine. My gut feeling is that makes it harder to debug the code that's gone wrong, because by the time you see the exception, you could be quite a long way away from the code that originally caused it. It's a bit like using as instead of a cast.
Oh, I don't like brackets, so I'll just use var x equals some object as string. Because that's safe, it's not going to throw an exception. The fact that I later use x.length and expect it to be non-null, unconditionally, but still I'm using as, so it's safe, right? No.
If there's something that you think cannot possibly go wrong, find out as soon as you can. So this is a non-async method that only validates the arguments and then calls into a genuine async method. Here we have async task of decimal calculate worth async.
If you've done any iterator blocks, if you've reimplemented linked objects, and if you haven't, do so. It's really fun. This pattern would be familiar to you. So this is what select looks like. We have a normal method and then one using an iterator block. If you understand why we do that
and why this one, this authentication exception, is within the async method.