0

I just learned some more about async await. I used to think everything in an async Task method just gets executed on a background thread. Now I understand that's not the case and went to refactor some of my code so its blocking less of my UIThread (WPF).

This is my "normal" async method:

public async Task<List<Data>> DoSomethingAsync(){
    var data = dbContext.Data.ToListAsync(); // <- Executes on a background thread internally
    ... // <- Executes on UIThread
    return dataList;
}

If I call that method in a ViewModel now, it will not actually be executed on a background. To do that I have to call Task.Run(DoSomethingAsync);.

I didnt want to just blindly wrap all my calls to async methods into Task.Run, so I found myself checking the implementation of every single method whether it has "..." content or is just wrapping an async method from ef core/http/filesystem.

What is the general idea here? Should all my async methods just look like this by default?

public Task<List<Data>> DoSomethingAsyncActuallyInBackground() {
    return Task.Run(() => {
        var data = dbContext.Data.ToListAsync();
        ... 
        return dataList;
    }
}

I feel like the first method is somehow "lying". It says Async, but calling it I have actually no idea whether it is truly asynchronous/non blocking code. Internally ef cores ToListAsync() works on a background thread right? Thats how its possible for it to be non blocking.

I am now realizing that I equate "asynchronous" with "on a background thread / non blocking". Is this even true?

Uhmmer
  • 47
  • 4
  • 2
    `DoSomethingAsyncActuallyInBackground` is not a good idea . Let IO work be IO work, and if you have crazy cpu bound work that is blocking the UI thread. Use `Task.Run` strategically. – TheGeneral Nov 20 '20 at 23:52
  • 2
    You have some misconceptions about `async/await` and multithreading. You should consider reading [Tasks are (still) not threads and async is not parallel](https://www.wintellect.com/tasks-are-still-not-threads-and-async-is-not-parallel/) and/or [There Is No Thread](https://blog.stephencleary.com/2013/11/there-is-no-thread.html). – 41686d6564 stands w. Palestine Nov 20 '20 at 23:53
  • _"Internally ef cores `ToListAsync()` works on a background thread right?"_ **No**. _"Thats how its possible for it to be non blocking"_ Again, no. See the two linked blog posts for detailed explanation. – 41686d6564 stands w. Palestine Nov 20 '20 at 23:55
  • Does this answer your question? [How and when to use ‘async’ and ‘await’](https://stackoverflow.com/questions/14455293/how-and-when-to-use-async-and-await) – kapsiR Nov 21 '20 at 00:00
  • Think about mailing a letter with certified mail. You drop it in the mailbox without "creating an additional thread". Some time later, you get notified that the addressee received the letter - that's the nature of the post office "OS". That's how async I/O operations work; you say "here do this, let me know when it's complete". The Task represents the work that is to be done. When you `await` a task, your program returns a Task that represents the Task you are awaiting *plus* the rest of the code in your method that hasn't run yet. – Flydog57 Nov 21 '20 at 00:19
  • 1
    Take a look at these: [Should I expose asynchronous wrappers for synchronous methods?](https://devblogs.microsoft.com/pfxteam/should-i-expose-asynchronous-wrappers-for-synchronous-methods/) and [Task.Run Etiquette Examples: Don't Use Task.Run in the Implementation](https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html). TL;DR: *"Asynchronous methods should not be exposed purely for the purpose of offloading."* – Theodor Zoulias Nov 21 '20 at 00:33

1 Answers1

2

I am now realizing that I equate "asynchronous" with "on a background thread / non blocking". Is this even true?

Asynchronous does mean "nonblocking", but "asynchronous" (in the modern sense of the word, as used by async/await) does not necessarily mean "on a background thread".

I recommend starting with my async intro and following up with There Is No Thread. Consider the case of a database query: once the query is sent to the db server, there's no need for a thread to just sit there blocking, waiting for a response. Asynchronous code frees up that thread to do other things, and uses a Task<T> (a.k.a. "promise") to notify the application when the query results have arrived from the db server.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Is there such a thing as being "too async"? Take for example the XmlWriter class with its WriteXXXAsync methods that would typically write out just a few bytes at a time. It would seem that the overhead of awaiting each individual call would greatly outweigh just a single await on an XElement.WriteToAsync for example. – mford Nov 23 '20 at 16:24
  • Yes, there is such a thing as "too async". The "chatty" interfaces like that can cause performance problems if called at scale. `ValueTask` mitigates these performance problems, but methods that do very little I/O and also return `Task` can be problematic. I usually try to do formatting in-memory and then (asynchronously) write out the complete string all at once. – Stephen Cleary Nov 23 '20 at 16:28
  • That's what I figured, and was very surprised to see those types of methods implemented in the XmlWriter class. Now I'm curious as to why they thought it necessary to include them. – mford Nov 23 '20 at 16:34
  • @mford: I have seen multi-GB XML files that require streaming. Sad, but true. – Stephen Cleary Nov 23 '20 at 16:38