Correcting Common Async/Await Mistakes in .NET - Brandon Minnick

Sdílet
Vložit
  • čas přidán 9. 07. 2019
  • Did you know that the .NET compiler turns our async methods into classes? And that .NET adds a try/catch block to each of these classes, potentially hiding thrown exceptions? It's true!
    In this session, we will learn how to best use async/await in C# by analyzing how .NET compiles our async code.
    Join me as we take an existing app and optimize its async code together, showing off performance gains, better exception handling, improved run-time speed, and smaller app size!
    Check out more of our talks in the following links!
    NDC Conferences
    ndcoslo.com
    ndcconferences.com
  • Věda a technologie

Komentáře • 120

  • @Miiite
    @Miiite Před 4 lety +23

    This is a terrific presentation. Detailed, technical, yet exposes simple examples. Just love it, thanks

  • @NickBullCSharp
    @NickBullCSharp Před 3 lety +11

    This has definitely been eye opening. There's a lot of great information here. This'll most definitely help with a lot of the code I am currently writing. Thanks for uploading :)

  • @IvarDaigon
    @IvarDaigon Před 3 lety +16

    One thing completely missing from this video is that you should make a habit of not passing Lists or Dictionaries into Async code because they aren't thread safe and you have no real way of knowing ahead of time which thread may be accessing or modifying them. Even if you don't intend on modifying them in your code, some developer in the future may do so and it will cause all sorts of problems. It is far safer to just get into the habit of using any of the System.Colllections.Concurrent types (CuncurrentDictionary etc) by default.
    I would also argue that handling errors and logging them within your functions themselves and using the Try pattern (like TryAdd TreyRemove etc where you return success or failure rather than throwing exceptions) is far safer and easier to debug and maintain than using extension functions that allow people to call Async void function whenever they like and then catch any errors. The later assumes that the developer calling the function is aware of all of the different types of exceptions that can be thrown which is almost never the case and you lose access to the stack frame variables so putting a break point in your catch block outside of the function call will yield almost no helpful information.
    Async void should really only be used for event handlers that you have no control over such those auto generated when you double click in UI elements in visual studio. It is for backwards compatibility only and should be treated that way.
    Final note:
    Setting ConfigureAwait(true) as the default option was a poor design choice on the part of Microsoft.
    If code is not directly touching UI elements then it usually does not need to return to the same context from whence it game. This is true for about 99% of the await calls in a typical application so developers have to put ConfigureAwait(false) all over their code which is not only very tedious but also makes the code look very ugly.

  •  Před 4 lety +1

    I've learned something today so great talk.

  • @ocallesp
    @ocallesp Před 4 lety +1

    For new C# sharp developers this video is good ! thanks

  • @andreyka26_se
    @andreyka26_se Před 4 lety +22

    Come on, people, You all need to read Richter. Async is not about multithreading at all. Async is about forcing your thread to do something when your network card's driver do all job, or when your disk's driver do all job. it is not important what thread will continue execution(like the speaker said on the first sample)./ Event thread1 can continue execution if it gets from the thread pool. It doesn't matter at all.

    • @nikhilrathore2385
      @nikhilrathore2385 Před 2 lety

      But isn't multi threading also the same. Multiple threads running on single CPU core. The core keeps switching between multiple tasks (threads) either because a thread is making a network call or some I/o operation. The way I look at it, async await looks similar. You just continue executing the non dependent code without waiting for the response (Context switching). Could you please elaborate

    • @andreyka26_se
      @andreyka26_se Před 2 lety +2

      @@nikhilrathore2385 no, it is not the same. Async is about moving job to other device from CPU, or more precisely: making CPU not to wait until something is being done by other device.

    • @nikhilrathore2385
      @nikhilrathore2385 Před 2 lety

      @@andreyka26_se by other device do you mean other CPU core? If yes, then how is it possible since we have limited cores(4 or 8 mostly) and we run multiple apps together with the apps themselves doing lot of things (like browser handling multiple tabs).

    • @andreyka26_se
      @andreyka26_se Před 2 lety +1

      @@nikhilrathore2385 no, I mean, I/O devices: drive, network card, etc. CPU cannot read the file, as I know, the specific driver does it. The same is for network card, and other I/O stuff.

    • @nikhilrathore2385
      @nikhilrathore2385 Před 2 lety

      @@andreyka26_se yes that makes perfect sense. But now in multi threading also the same thing happens. single core switches between 2 or more tasks. So if we consider 1 CPU core, both multithreading and async says that a single physical worker(CPU core) will switch between those task whenever 1 task doesn't need CPU for some time, like when it needs to do some io or network call. Is the difference just that in async, we just tell that which code needs to wait for the blocker to be resolved and which code is independent (through callbacks in terms of javascript). while in multithreading we are in complete charge of making the tasks and allocating them to threads and we are ones who decide when will context switch happens. Becuase end of the day we have 1 core, 1 single worker that is managing multiple task by switching between them and also at any point of time, it can just do atmost 1 operation. So the difference is still not clear to me

  • @MobilTemp
    @MobilTemp Před 4 lety +1

    Amazing video, great tips.

  • @Moosa_Says
    @Moosa_Says Před 8 měsíci

    Honestly best talk ever !! thanks a lot.

  • @user-xd1su3sk3i
    @user-xd1su3sk3i Před 3 lety

    Very clear and useful. Thanks.

  • @mieszko5260
    @mieszko5260 Před 2 lety

    Did I understand correctly - it is better practive to always declare interfaces with ValueTask instead of Task ?

  • @caractacustube
    @caractacustube Před 4 lety +13

    At 0:06:00 you say that the await keyword spawns a new thread from the thread pool? This is incorrect. Only an awaited IO-bound takes will return the thread, and invoke the continuation on an IO completion thread.

  • @jasonracey9600
    @jasonracey9600 Před 2 lety +1

    This is the first time I feel like I understand the purpose of ConfigureAwait(false), thanks.

  • @MayankGupta303
    @MayankGupta303 Před 3 lety

    One word. Amazing

  • @Tyrrrz
    @Tyrrrz Před 4 lety +47

    Thinking about async in terms of thread pool is conceptually misleading

    • @sgerar37
      @sgerar37 Před 4 lety +13

      I agree 100% with your thought; unfortunately, the async-await machinery is one of the leakiest abstractions I have ever used in programming and IMHO the current implementation (relying on a state machine that is typically executed by the thread pool) shouldn't exist in an OS that offers recursive locking constructs like Windows. We recently experienced a deadlock nightmare by some sync code I migrated to async. The original code was calling some third-party code, that was labeled thread-safe, but unfortunately (as we had to find out using Windbg) it was synchronized using recursive locking. The third-party code assumed that a sequence of lock-step related operations would always be executed by the same thread. Our unit-test code completely missed this problem, since the async calls were not awaited with ConfigureAwait(false) and the test ran inside a synchronization-context (forcing the continuations to be run on the same thread and hiding the problem). So my point is you REALLY REALLY need to be aware of the implementation details in order to avoid shooting your leg, or if you have done (as in my case) be able to understand why you blew it off.

    • @darkopz
      @darkopz Před 7 měsíci

      Misleading maybe. Important to realize, absolutely. It’s possible to write long running tasks with async/await that eat the entire ThreadPool. And it’s not obvious.

  • @roodborstkalf9664
    @roodborstkalf9664 Před 4 lety +1

    Excellent

  • @marounbassam
    @marounbassam Před 2 lety +4

    In the slide where you showed the usage of "ConfigureAwait", isn't it enough to use it only for the first call? It'll be returned to a different thread, and I don't care if that different thread is awaiting again, it's already not the UI thread that I didn't want it to await.

    • @AyuNeko
      @AyuNeko Před rokem

      i was thinking the same thing. I think it is enough to use it only for the first call to free up the UI thread. But if you don't use it later down the line, it always waits until that one caller thread is available instead of using just any thread which is available, which would be more performant i think. not sure though....

  • @samueldebeer2306
    @samueldebeer2306 Před 2 lety +8

    This is incorrect. A new thread is not created to simply await the task. The thread executing the method reaches the I/O bound method that is awaited and is then freed up to do other things until the task is completed, since there is nothing the CPU can do except wait for this task to be completed by an external 'computer' somewhere else. The idea that another thread is brought in to wait for the task to complete, thus blocking itself from doing anything, would be a bit pointless, as you'd still be wasting resources.
    If I'm not mistaken, the presenter is describing a scenario that is closer to calling Task.Run(), which would use another thread to run whatever code you'd like, which in this case wouldn't make sense, as the DownloadDataTaskAsync method being executed is I/O bound and the thread you've created to await the I/O bound task, like I said, would just sit and do nothing.

  • @ferzik1508
    @ferzik1508 Před 4 lety +5

    @58:58 Task.Run() will put code on different threadpool thread ...

    • @gabeanthony8910
      @gabeanthony8910 Před 3 měsíci

      Yes. It is scheduled on a threadpool. It might run on the same thread, or it might not. But the statement the presenter makes "dotnet isn’t going to put that task on a thread..because we didn't use the await keyword" is wrong, I feel. It confuses an already confusing topic.

  • @drullo
    @drullo Před 3 lety

    Good information. And Brandon sounds exactly like John Krasinski from The Office.

  • @duke227
    @duke227 Před 4 lety

    What tool did you use to display the compiler generated code?

    • @keja0
      @keja0 Před 4 lety +2

      I use the ILSpy addon for VisualStudio/Code

  • @figloalds
    @figloalds Před 4 lety +2

    Forgot to say that uncaught Task exceptions bricks the program when the Task is garbage collected;
    And also that .Wait() and .Result will may cause crazy deadlocks

  • @joephillips6634
    @joephillips6634 Před 4 lety +8

    at 33:23 you should probably just call the refresh method after constructing the class IMO

    • @dogpixels
      @dogpixels Před 4 lety +1

      I hit the same question just yesterday (wanting an app to load some stuff upon start), and my answer was simply to hook up the ExecuteRefreshCommand() to the MainWindow's OnLoaded event. Wish the presenter would have mentioned that simple solution to a common question.

    • @KoScosss
      @KoScosss Před 3 lety +1

      Or use async static factory method

  • @volan4ik.
    @volan4ik. Před 4 lety +4

    I'm surprised that nobody asked about ContinueWith method which can easily replace the FireAndForget call. It brings the support for exception handling of non-awaited calls, for continuation of parent task so we can easily handle result if task succeeded.
    Also nobody remembered about Task.Factory.ContinueWhenAll and Task.Factory.ContinueWhenAny methods

    • @SpaceShot
      @SpaceShot Před 4 lety +1

      I'm not sure how I feel about the FireAndForget advice at all. In ASP.NET Core just about everything has an async interface or implementation where you wouldn't need this. In .NET Framework the lagacy frameworks predated async/await so there are scenarios where you are in a sync method and want to fire off something async. I think I like a technique where that is fired off as a Task that can be awaited by interested code that wants to know that Task was completed or not.

    •  Před 4 lety

      Well the talk is about async/await and I guess he wanted to focus on that and not on the entire Task system which is much richer.

  • @serhiihorun6298
    @serhiihorun6298 Před 4 lety

    Thanks

  • @khanabadosh_shekhar
    @khanabadosh_shekhar Před 4 lety +1

    54:30 I was wondering the same

  • @aresagathos2075
    @aresagathos2075 Před 4 lety +1

    I did this example with HttpClient, with dependency injection calling the async function from a constructor, and i was unable to catch the exception, despite i was awaiting the example.
    Still don't know why the state machine doesn't rethrow the exception to date.

    • @symix.
      @symix. Před rokem

      I know its been 2 years but... how did you await inside constructor? it isnt possible..?
      And if it was not awaited, then it doesnt matter if you awaited inside it.

    • @aresagathos2075
      @aresagathos2075 Před rokem

      @@symix. Task.Run. And meanwhile i know that c# can't catch exceptions in this specific cases.

  • @miamivicefan
    @miamivicefan Před 4 lety +1

    This conf is interesting (avoid async void, use configureawait ok) but i'm not sure why and when use AsyncCommand?

    • @volan4ik.
      @volan4ik. Před 4 lety

      While using MVVM pattern (usually in WPF applications)

    • @volan4ik.
      @volan4ik. Před 4 lety

      Why - because usual ICommand, which is a part of pattern, is only synchronous. And we may need to have a mechanism of running async code as well (together with bindings etc).

  • @jamesnorbury8661
    @jamesnorbury8661 Před 4 lety +7

    Great talk with some good advice!
    However, I don't like the fire and forget call to an async void method in a constructor 31:41. The case made is that because the code inside the async void method uses await, any exception which is thrown will surface correctly, so it is fine to do. While it is true that exception handling is correct; there is a possibility that a slow network connection will mean this newly constructed class is used before it is ready. E.g. a list initialized to the result of an API call, a NullReferenceException would be thrown if trying to access an item from this list before the async method completed.
    It seems more reasonable to await calls to the API before creating the view model, then pass the results into the view model's constructor so it is instantiated correctly. This method doesn't lock the UI thread and seems safer to me than calling a fire and forget in a constructor.
    While I don't doubt that in the current situation this fire and forget won't cause any issues, I wouldn't say it was a best practice but more of an interesting edge case. I could see this being useful for truly fire and forget tasks like adding something to a queue.

    • @user-il5fc2qk6t
      @user-il5fc2qk6t Před 4 lety

      James Norbury Yeah, that's a great point!

    • @SpaceShot
      @SpaceShot Před 4 lety

      I have also been thinking about a scenario like a "web server startup" where it was common practice to do things in global.asax methods. Those methods existed before async/await so people often block while making sure dependencies are in place. I think you could also create a Task with that initialization and then await it where it is needed (since the task is complete, await is almost a no-op). However, using modern frameworks in ASP.NET Core, this concern goes away.

  • @PanzerFaustFurious
    @PanzerFaustFurious Před 4 lety

    at 44:50 , should we use ValueTask if we might not hit await, or should we use ValueTask if we expect to not hit await often?
    Because, lets say we have an API that returns badrequest when the modelstate is invalid. This doesnt happen often, but still, the api might not hit await. Should we still use ValueTask?

    • @Miggleness
      @Miggleness Před 4 lety

      Best example is with in-memory caching. When your async method is more likely to return a result that is already in memory rather than do an async operation (I/O), then using ValueTask is more appropriate since you dont allocate a Task on the heap. In the event that fetch data from your asynchronously, ValueTask will create a Task since we now need to store the state on the heap.

    • @Miggleness
      @Miggleness Před 4 lety

      For your use case, use Task still. I imagine that the aspnet core middleware could call await on your async API method more than once. Whereas, you can await on a ValueTask just once

  • @TheAceInfinity
    @TheAceInfinity Před 7 měsíci

    13:17 I wouldn't say it's "gone forever".. It's just placed on the Task object. Continuation tasks also have a nuance in that it's a different task so awaiting the continuation task means that the original task's exception does not propagate to be thrown.

  • @baz5719
    @baz5719 Před 4 lety +3

    Great speaker.

  • @chadiusmaximus9350
    @chadiusmaximus9350 Před 2 lety

    not a bad talk but I'd like to point out that the compiler only generates a class in debug mode. in release it generates a struct. i.e it performs better because it's running on the stack.

  • @konstantinsarychev9305
    @konstantinsarychev9305 Před 4 lety +2

    Ridiculous optimization with ValueTask taking into account that new class is created each time we use await. If I got it correctly.

    • @volan4ik.
      @volan4ik. Před 4 lety

      Class is created for every method marked with async keyword, not for every await keyword. 'await innerTask' is compiled into assignment of inner.GetAwaiter() and into changing the state.
      ValueTask does make sence when we return from async method before any 'await' is called, so less allocations are needed.

  • @JustinRomaine
    @JustinRomaine Před 4 lety

    If you are not using the await keyword and just returning a Task then whats the point in making the method a task at all.
    Seems there is no point in async when you are not waiting form multiple tasks to complete simultaneously within a method

  • @mattiasmartens9972
    @mattiasmartens9972 Před rokem

    does safeFireAndForget() offer any advantage if there is no handler supplied to onError?

    • @BBTRaziel
      @BBTRaziel Před 10 měsíci +1

      From what I understood from the video, the only purpose in that case would be to avoid future mistakes while maintaining the code. Because it would make it very clear that you are "setting free" the Task on purpose. From a practical and technical point, there is no difference in the execution of the code

  • @Seedzification
    @Seedzification Před 4 lety +13

    If you need to put configureAwait on every "await" keywords, that's a design flaw imo.

    • @SpaceShot
      @SpaceShot Před 4 lety +8

      You only use it if you don't care about returning to the original context. In app development, you usually do... because you want to get back to the GUI thread on desktop or to a thread with the http request context restored on web servers. If you are writing libraries, including libraries for your own consumption, then you usually DON'T need to return to the same context. There was some talk about adding features where you might "configure" for a whole project or class or namespace, but you figure the general consumption case here is the app developer. The library developer does need to learn a bit more, yes.

  • @gregorymorse8423
    @gregorymorse8423 Před 4 lety

    So what if you need to do 100 asynchronous operations and finally return to the calling thread at the end. Obviously the first ConfigureAwait false has lost context. So it seems you use 100 ConfigureAwait false in a task and call that task with ConfigureAwait true in the one place returning to the calling thread. This way far less synchronization takes place than using ConfigureAwait true 100 times.

  • @F2H16
    @F2H16 Před 4 lety +2

    Unless I'm missing anything , "The Result property is a blocking property. If you try to access it before its task is finished, the thread that's currently active is blocked until the task completes and the value is available. In most cases, you should access the value by using Await or await instead of accessing the property directly" says docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2013/hh524395(v=vs.120)
    So, var result = await task1 and var result = task1.Result are not identical.

  • @joephillips6634
    @joephillips6634 Před 4 lety

    at 22:05, does it really jump back to the original thread or does it just store the context and copy that over to a thread but not necessarily the original one?

    • @alexandertkacuk8712
      @alexandertkacuk8712 Před 4 lety

      I would like to know it too.
      And i have not found the location in the source code yet.

  • @Tydides64
    @Tydides64 Před 4 lety +12

    How does a developer advocate at Microsoft think that awaited methods run at new thread?
    They don't. Read some Stephen Cleary please, this is embarrassing.

  • @Stashix
    @Stashix Před rokem

    Does Developer Advocate mean he'll represent me when I go to trial for all my coding transgressions?

  • @sach2372
    @sach2372 Před 4 lety +1

    Resource link - codetraveler.io/ndcoslo-asyncawait/

  • @sumitsharma5537
    @sumitsharma5537 Před 4 lety +1

    Fantastically Insane .

  • @TellaTrix
    @TellaTrix Před 9 měsíci

    Wow! Finally, got to know how bad developer am I?

  • @igorsentrxigorsentrx5550
    @igorsentrxigorsentrx5550 Před 3 lety +3

    How presenter at conference could make so many mistakes ?! How video with all pointed mistakes could receive such a high like\dislike rate ?!

  • @mohamedauf3668
    @mohamedauf3668 Před 4 lety +1

    One should highlight, that he's talking about .NET only. Mechanisms are different in (asp).net core, e.g. there's no SynchronizationContext in asp.net core.
    blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html

  • @dotnetdevni
    @dotnetdevni Před 3 lety

    How do i fix this its a nightmare
    A database operation failed while processing the request.
    InvalidOperationException: A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see go.microsoft.com/fwlink/?linkid=2097913.

  • @VareneVino
    @VareneVino Před 11 měsíci

    Do NOT use ConfigureAwait(false). The default is there for a reason and that reason is Context!

  • @owickedfox
    @owickedfox Před 2 lety

    For me, it would be 100/100 if he used CancellationToken in every async function.

  • @marcelg861
    @marcelg861 Před 4 lety +13

    .ConfigureAwait(false) 🤯

    • @philipmrch8326
      @philipmrch8326 Před 4 lety

      I rarely use it, as sometimes I have events that will be invoked after an await, and I want it to run on the original context (if there is a SynchronizationContext/TaskScheduler)

    • @volan4ik.
      @volan4ik. Před 4 lety +1

      @@philipmrch8326 TaskScheduler is not the same as SynchronizationContext.
      Follow the link to get a great explanation: stackoverflow.com/a/55085418

  • @TheLaucomm
    @TheLaucomm Před 4 lety +20

    Though there continue to be correct practical advice in his talk, he still hasn't understood how async await actually works. He still incorrectly assumes that there is a connection between await and spawning another thread and other details that are just wrong.
    Async await isn't about multi threading, its about I/O bound operations. The whole idea is about avoiding the use of multiple threads. Only just by it's nature, async await CAN also be used in multi threaded scenarios, but that's not the main case, just a benny.
    This is explained in a really simple way, that anybody can understand in this youtube video: czcams.com/video/hB0K1JWFoqs/video.html

    • @mounirbenhalla4191
      @mounirbenhalla4191 Před 4 lety

      thanks for video,, its the true story for await and async

    • @razibtx
      @razibtx Před 2 lety +1

      Thank you..thank you thank you..I was looking for this for last several days. thank you.

    • @brandonminnick
      @brandonminnick Před rokem +1

      > he still hasn't understood how async await actually works
      I do. Promise.
      Async/Await is an incredibly complex topic and this presentation is limited to 60 minutes.
      I had to purposefully omit delving into certain topics. The discussion on thread spawning could be a 60-minute talk on its own.

  • @buddysnackit1758
    @buddysnackit1758 Před 2 lety

    Let's Go Brandon!

  • @ZdenoTirc
    @ZdenoTirc Před 4 lety +11

    Ah, many misconceptions. Looks like speaker doesnt understand async await very well.

  • @Micke2nd
    @Micke2nd Před 2 lety

    When we already speak about wrong using, why waste time of the audience with an introduction into basics like multithreading, etc. ?

  • @framepointer
    @framepointer Před 2 lety +1

    .ConfigureAwait(false) is really poor design.

  • @csexton07
    @csexton07 Před rokem

    C# its Id not ID and make async methods with the Async suffix so that way I can read it, oh this is an async method.

  • @ankhayratv
    @ankhayratv Před 4 lety +6

    To any speaker: do not drink unless you really need to!

  • @tehklevster
    @tehklevster Před 2 lety +1

    This is fine if developing a desktop UI app. You didn't cover the benefits or otherwise when building web apps, for which I can see no advantage. Yet I see this all the time in our codebase because cargo cult. Also some MS API's force you to use async (some of the Identity packages), and it generally annoys me in a web app where we're doing nothing clever.

  • @maksymkazakov221
    @maksymkazakov221 Před 4 lety +4

    An interesting conf, but drinking sound is really annoying.

  • @rentefald
    @rentefald Před 4 lety

    First signs of code that will later work in harmony of AI, that eventually will lead to the Terminator.

    • @garryiglesias4074
      @garryiglesias4074 Před 4 lety

      Are you lost and this is your first programming encounter ?

  • @MadaFakaTOO
    @MadaFakaTOO Před 3 lety +5

    So they're letting absolutely anyone do a talk now, huh?

  • @belowasmelashgebremariam

    Kemey ke

  • @radicalbyte
    @radicalbyte Před 3 lety +4

    Sorry, I can't get over the American YO BRO college guy voice, it triggers my "recruiter bulls***" filter.

    • @washedtoohot
      @washedtoohot Před 2 lety +1

      Lol. The guy is still legit though

    • @samueldebeer2306
      @samueldebeer2306 Před 2 lety +1

      @@washedtoohot He fundamentally misunderstands async await when he says a new thread is created to await the I/O bound task.

    • @brandonminnick
      @brandonminnick Před rokem

      I'll try to be born with a different voice next time

  • @belowasmelashgebremariam

    AnneAsmelash

  • @shenth27
    @shenth27 Před 3 lety

    Microsoft guy using Apple computer..

  • @belowasmelashgebremariam

    Ewe Anne Asmelash Eye

  • @belowasmelashgebremariam

    EweNattey

  • @jacksonstevemartinez9468

    Kose Nanat

  • @belowasmelashgebremariam

    EweAsmelash

  • @belmiris1371
    @belmiris1371 Před 4 lety +1

    Wow. So much excitement about making code less and less readable and maintainable. "Here's a new magic thing-a-majig that does a thing you already have 10 other (clearer) ways of doing but it's NEW!" Depressing.