0

While the question may seem to have been asked multiple times and already have highly rated answers to me, I would like to suggest that multiple answers are conflicting and I can never come to a complete understanding of the internals of asynchronous code. I fully understand that it means to continue sequential code execution and complete the task later, I am trying to understand that later portion.

Answer 1 - Suggests UI/Main

Firstly, this question contained the following test inside of it, suggesting that asyncrhonous code runs on the Main/Ui thread and links an article explaining why there is no other thread for asynchronous code.

Q: "In my mind, since I do mainly UI dev, async code is code that does not run on the UI thread, but on some other thread. "

A: That belief is common but false. There is no requirement that asynchronous code run on any second thread.

Infact the answer suggests that asynchronous programming with threads is "wrong" by saying, "Threads are workers. Asynchronous workflows can happen all on one thread. The point of the asynchronous workflow is to avoid hiring more workers if you can possibly avoid it."

Answer 2 - Suggests UI/Main

The next question I have read also suggests that Asynchronous code is ran on the Main/UI thread, however the comparison used is JavaScript, which we all know is a single-threaded language. For example, suppose I run this code,

 function wait(ms) {
  var start = Date.now(),
      now = start;
  while (now - start < ms) {
    now = Date.now();
  }
}
setTimeout(() => {
    wait(5000); 
}, 3000)

setTimeout will be called Asynchronously and after 3000ms the callback will be added to the Event Loop and eventually ran. However the method execution will be done on the Main/UI thread and thus, resulting in the UI being frozen for 5000ms.

Answer 3 - Suggesting new thread

This answer suggests that Asynchronous code is ran into a new thread, inside the answer he says. "When you execute something asynchronously, you can move on to another task before it finishes. That being said, in the context of computers this translates into executing a process or task on another "thread."

Answer 4 - Suggesting new thread

The final answer suggests that Asynchronous code is thread based by saying, "In the general case, an asynchronous call does not necessarily create a new thread. That's one way to implement it, with a pre-existing thread pool or external process being other ways. It depends heavily on language, object model (if any), and run time environment. Asynchronous just means the calling thread doesn't sit and wait for the response, nor does the asynchronous activity happen in the calling thread.", with the main point being "nor does the asynchronous activity happen in the calling thread"

I am not sure where to go from this point, from my understanding I would think Asynchronous code must execute in a different thread, to never block the UI, with the only exception being JavaScript, I suppose. However, even when it comes to the execution of Asynchronous code on a single threaded language, I would have thought that any function callback that is ran, must be ran to completion before another callback is ran.

The second answer on here suggests by drawing, when Asynchronous code is ran on a single thread, it stops and runs a different Asynchronous callback, switching back and forth, much like a thread would until they're finished. This to me doesn't make complete sense since with Asynchronous code, they generally have a callback included to prevent any sort of race condition, suppose B required A to run to completion first, B would be a callback inside of A. The callbacks A and B would not "run together" and switch between each other.

cassandrad
  • 3,412
  • 26
  • 50
user12388651
  • 179
  • 1
  • 8
  • `I would think Asynchronous code must execute in a different thread` - no. https://stackoverflow.com/q/17661428/11683, http://blog.stephencleary.com/2013/11/there-is-no-thread.html. Some people from the questions you've quoted confuse threads with tasks or even see them as the same thing, whereas async has nothing to do with threads. `when Asynchronous code is ran on a single thread, it stops and runs a different Asynchronous callback, switching back and forth` - https://stackoverflow.com/q/37419572/11683 – GSerg May 26 '20 at 06:48
  • The question "`Does Asynchronous code run in the UI thread or a new/different thread to not block the UI?` " depends on which implementation of which language. You'd expect (potentially) different answers for C#, Javascript and "conceptual theory". – Brendan May 26 '20 at 06:54

1 Answers1

1

Let's work from "low level" up.

For low level hardware, most modern devices (network, disk) use bus mastering or DMA to do transfers to/from the computer's main memory and generate an IRQ when the transfer is complete; and supports a "driver issues command to device, then device issues IRQ when command completes" model for things that don't involve IO. For an example; if you want to (e.g.) asynchronously read some data from disk on a single-CPU computer; the CPU (device driver) can tell the disk controller hardware what to do, then the CPU can continue doing useful work while the data is being transferred by the disk controller, then when the IRQ arrives (signalling that the transfer completed) it can trigger some kind of "asynchronous transfer completed" action.

If the device doesn't support this (e.g. must use some kind of "programmed IO" where a CPU needs to be used for transferring data, etc) then the asynchronicity would have to be emulated somehow - by using another CPU, by lying (doing it synchronously and pretending "asynchronous completion" occurred immediately), or by using a single CPU that rapidly switches between different work (one that asked for the asynchronous operation and another that is doing the asynchronous operation) to create the illusion of asynchronicity.

The next layer up is the operating system's kernel and device drivers. For almost all modern operating systems, everything that takes enough time to be worth caring about is (internally) fully asynchronous (even when the underlying hardware doesn't support it, asynchronous operations are emulated); primarily because of multi-processing and performance (e.g. ensuring that separate requests from separate processes are able to get all hardware devices doing useful work in parallel if/where possible, often with IO priorities to try to ensure more important work happens sooner than unimportant work, often combined with various prefetching strategies happening in the background to increase the chance that work is done before any process asks for it). However; the API given to user-space may not expose the kernel's support for asynchronous operations to user-space processes. For example, originally the POSIX API didn't support asynchronous operation at all (it was retro-actively hacked on top after it was too late, which is why you still can't even do basic stuff like open a file asynchronously).

The next layer up is the language's run-time. This is wedged between the language's standard (which might or might not cater to asynchronous operations and might or might not have some kind of "user-space threading/fibers/whatever" going on) and the kernel's API (which might or might not provide some/any asynchronous operations). For cases where the language's run-time supports asynchronous operations but the underlying kernel API doesn't, it has to be emulated somehow - e.g. using (one, more?) "kernel threads" (and possibly hiding that behind any kind of "user-space threading/fibers/whatever"), or by lying (doing it synchronously and pretending "asynchronous completion" occurred immediately).

The final layer is third-party libraries/modules/packages to add asynchronous operations on top of language that didn't support them originally. This always involves some kind of emulation.

Note that if you look at the entire set of layers, the system as a whole could be emulating asynchronous operations at 2 different levels (in the kernel/at the device driver, plus in user-space at language run-time or on top of it); so 2 different methods of emulating asynchronicity could be being used at the same time (and even in that case neither might be using threads/tasks to emulate/provide asynchronicity).

Of course if there's 100 languages and an average of 4 implementations of each language; then there would be 400 permutations that might all have different answers to how asynchronous operations are actually implemented; so you can expect (potentially) different answers for C#, Javascript, ... which may be correct (for that implementation of that language) despite being wrong (for other implementations of that language or other languages).

Brendan
  • 35,656
  • 2
  • 39
  • 66