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

Поділитися
Вставка
  • Опубліковано 15 гру 2024

КОМЕНТАРІ • 121

  • @Miiite
    @Miiite 5 років тому +23

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

  • @caractacustube
    @caractacustube 4 роки тому +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.

  • @andreyka26_se
    @andreyka26_se 5 років тому +23

    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 2 роки тому

      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 2 роки тому +3

      @@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 2 роки тому

      @@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 2 роки тому +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 2 роки тому

      @@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

  • @NickBullCSharp
    @NickBullCSharp 4 роки тому +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 3 роки тому +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.

  • @ferzik1508
    @ferzik1508 4 роки тому +5

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

    • @gabeanthony8910
      @gabeanthony8910 10 місяців тому

      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.

  • @joephillips6634
    @joephillips6634 5 років тому +8

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

    • @dogpixels
      @dogpixels 4 роки тому +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 3 роки тому +1

      Or use async static factory method

  • @Tyrrrz
    @Tyrrrz 5 років тому +47

    Thinking about async in terms of thread pool is conceptually misleading

    • @sgerar37
      @sgerar37 4 роки тому +14

      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 Рік тому

      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.

  • @TheAceInfinity
    @TheAceInfinity Рік тому

    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.

  • @jasonracey9600
    @jasonracey9600 3 роки тому +1

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

  • @marounbassam
    @marounbassam 3 роки тому +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 2 роки тому

      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 2 роки тому +10

    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.

  • @ocallesp
    @ocallesp 4 роки тому +1

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

  • @volan4ik.
    @volan4ik. 5 років тому +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 5 років тому +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.

    •  5 років тому

      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.

  • @jamesnorbury8661
    @jamesnorbury8661 5 років тому +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.

    • @ЕвгенийМальцев-ш6в
      @ЕвгенийМальцев-ш6в 5 років тому

      James Norbury Yeah, that's a great point!

    • @SpaceShot
      @SpaceShot 5 років тому

      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.

  • @Moosa_Says
    @Moosa_Says Рік тому

    Honestly best talk ever !! thanks a lot.

  • @figloalds
    @figloalds 4 роки тому +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

  • @Tydides64
    @Tydides64 4 роки тому +13

    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.

  • @mieszko5260
    @mieszko5260 2 роки тому

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

    • @yangguchu1362
      @yangguchu1362 3 години тому

      No. Only use ValueTask when you have hot path

  • @aresagathos2075
    @aresagathos2075 4 роки тому +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. 2 роки тому

      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 2 роки тому

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

  • @konstantinsarychev9305
    @konstantinsarychev9305 5 років тому +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. 5 років тому

      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.

  • @PanzerFaustFurious
    @PanzerFaustFurious 5 років тому

    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 5 років тому

      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 5 років тому

      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

  • @Seedzification
    @Seedzification 5 років тому +13

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

    • @SpaceShot
      @SpaceShot 5 років тому +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.

  • @chadiusmaximus9350
    @chadiusmaximus9350 3 роки тому

    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.

  • @mattiasmartens9972
    @mattiasmartens9972 2 роки тому

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

    • @BBTRaziel
      @BBTRaziel Рік тому +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

  •  5 років тому +1

    I've learned something today so great talk.

  • @JustinRomaine
    @JustinRomaine 5 років тому

    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

  • @F2H16
    @F2H16 5 років тому +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.

  • @miamiviceclips
    @miamiviceclips 5 років тому +1

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

    • @volan4ik.
      @volan4ik. 5 років тому

      While using MVVM pattern (usually in WPF applications)

    • @volan4ik.
      @volan4ik. 5 років тому

      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).

  • @joephillips6634
    @joephillips6634 5 років тому

    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 5 років тому

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

  • @Stashix
    @Stashix 2 роки тому

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

    • @brandonminnick
      @brandonminnick 2 роки тому +1

      I handle these on a case-by-case basis

  • @gregorymorse8423
    @gregorymorse8423 4 роки тому

    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.

  • @MobilTemp
    @MobilTemp 4 роки тому +1

    Amazing video, great tips.

  • @duke227
    @duke227 5 років тому

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

    • @keja0
      @keja0 5 років тому +2

      I use the ILSpy addon for VisualStudio/Code

  • @TheLaucomm
    @TheLaucomm 5 років тому +21

    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: ua-cam.com/video/hB0K1JWFoqs/v-deo.html

    • @mounirbenhalla4191
      @mounirbenhalla4191 4 роки тому

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

    • @razibtx
      @razibtx 3 роки тому +1

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

    • @brandonminnick
      @brandonminnick 2 роки тому +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.

  • @sach2372
    @sach2372 4 роки тому +1

    Resource link - codetraveler.io/ndcoslo-asyncawait/

  • @dotnetdevni
    @dotnetdevni 4 роки тому

    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.

  • @khanabadosh_shekhar
    @khanabadosh_shekhar 4 роки тому +1

    54:30 I was wondering the same

  • @АнтонБ-х9у
    @АнтонБ-х9у 4 роки тому

    Very clear and useful. Thanks.

  • @igorsentrxigorsentrx5550
    @igorsentrxigorsentrx5550 4 роки тому +4

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

  • @drullo
    @drullo 4 роки тому

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

  • @mohamedauf3668
    @mohamedauf3668 5 років тому +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

  • @MayankGupta303
    @MayankGupta303 4 роки тому

    One word. Amazing

  • @marcelg861
    @marcelg861 5 років тому +13

    .ConfigureAwait(false) 🤯

    • @philipmrch8326
      @philipmrch8326 5 років тому

      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. 5 років тому +1

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

  • @VareneVino
    @VareneVino Рік тому

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

  • @roodborstkalf9664
    @roodborstkalf9664 5 років тому +1

    Excellent

  • @baz_sh
    @baz_sh 5 років тому +3

    Great speaker.

  • @serhiihorun6298
    @serhiihorun6298 4 роки тому

    Thanks

  • @TellaTrix
    @TellaTrix Рік тому

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

  • @ZdenoTirc
    @ZdenoTirc 5 років тому +13

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

  • @owickedfox
    @owickedfox 2 роки тому

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

  • @buddysnackit1758
    @buddysnackit1758 3 роки тому

    Let's Go Brandon!

  • @framepointer
    @framepointer 3 роки тому +1

    .ConfigureAwait(false) is really poor design.

  • @sumitsharma5537
    @sumitsharma5537 4 роки тому +1

    Fantastically Insane .

  • @csexton07
    @csexton07 Рік тому

    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.

  • @Micke2nd
    @Micke2nd 3 роки тому

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

  • @Raizin-d8p
    @Raizin-d8p 4 роки тому +5

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

  • @radicalbyte
    @radicalbyte 3 роки тому +4

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

    • @washedtoohot
      @washedtoohot 3 роки тому +1

      Lol. The guy is still legit though

    • @samueldebeer2306
      @samueldebeer2306 2 роки тому +1

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

    • @brandonminnick
      @brandonminnick 2 роки тому

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

  • @maksymkazakov221
    @maksymkazakov221 5 років тому +4

    An interesting conf, but drinking sound is really annoying.

  • @tehklevster
    @tehklevster 3 роки тому +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.

  • @ankhayratv
    @ankhayratv 5 років тому +6

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

  • @rentefald
    @rentefald 5 років тому

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

    • @garryiglesias4074
      @garryiglesias4074 4 роки тому

      Are you lost and this is your first programming encounter ?

  • @belowasmelashgebremariam
    @belowasmelashgebremariam 3 роки тому

    AnneAsmelash

  • @shenth27
    @shenth27 4 роки тому

    Microsoft guy using Apple computer..

  • @belowasmelashgebremariam
    @belowasmelashgebremariam 3 роки тому

    Ewe Anne Asmelash Eye

  • @belowasmelashgebremariam
    @belowasmelashgebremariam 3 роки тому

    Kemey ke

  • @belowasmelashgebremariam
    @belowasmelashgebremariam 3 роки тому

    EweNattey

  • @belmiris1371
    @belmiris1371 5 років тому +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.

  • @belowasmelashgebremariam
    @belowasmelashgebremariam 3 роки тому

    EweAsmelash

  • @jacksonstevemartinez9468
    @jacksonstevemartinez9468 4 роки тому

    Kose Nanat