0

Ok, let me try to restate the 2 questions:

  1. Does OS actively preempt a thread as soon as it starts blocking, and never return to the thread until blocking is done? I feel that the OS has the information about disk IO and network IO so it should have enough information to do so.
  2. If the OS can eliminate the CPU idle time by switching to another thread, do we really need asynchronous programming?
Ruminateer
  • 45
  • 1
  • 8
  • 2
    So in your theory, if a thread is not waiting for IO or network, it can halt the entire computer because there will never be a reason to switch from it. Please see [what actually happens](https://en.wikipedia.org/wiki/Preemption_(computing)). Optionally also see [what used to happen](https://en.wikipedia.org/wiki/Cooperative_multitasking). What you are describing does not happen. – GSerg Oct 02 '21 at 22:07
  • @GSerg I never said that a thread would not be preempted if it is not blocking. – Ruminateer Oct 02 '21 at 22:22
  • 2
    It is a required premise of your question, that the waiting on IO is a trigger for preempting. If you know that the OS will preempt a thread whether or not it is waiting for IO, then there is no reason for the OS to figure whether the thread is waiting, is there? It will be preempted anyway. It's like doing `if (condition()) then x = 1 else x = 1`, which can be simplified to `x = 1`. – GSerg Oct 02 '21 at 22:28
  • @GSerg I know that OS can preempt a thread regardless of whether the thread is blocking. But if the preempting policy doesn't take blocking into consideration then there would still be times when the CPU is blocking (if OS doesn't preempt the thread as soon as it starts blocking). If the OS actively preempts a thread as soon as the thread starts blocking, the CPU blocking time can be further reduced. What I am asking in the post is that if the OS is able to do so. – Ruminateer Oct 02 '21 at 22:50
  • 1
    So basically you are asking how thread schedulers in some OSes deal with threads in waiting state? There is no "the" thread scheduler, so the answer would be different per OS; for Windows, see https://www.microsoftpressstore.com/articles/article.aspx?p=2233328&seqNum=7 where it discusses transitions into and out from the Waiting state. – GSerg Oct 02 '21 at 23:02

2 Answers2

1

Therefore, even though the thread is blocking, the CPU is not blocking but running other threads.

Yes, that's correct.

If my understanding above is correct, what is the value of asynchronous programming?

It's useful if you want your program to make progress on multiple tasks simultaneously.

Of course you could get the same effect by explicitly spawning multiple threads yourself, and having each of them work on a separate task (including any blocking calls), and that would work as well, but then you have to explicitly manage all those threads which can be a bit of a pain to get right. (inter-thread synchronization/communication can be tricky, and in particular the case where you want to cancel an operation is difficult to implement well if one or more of your threads is blocked inside a blocking I/O call and thus can't be easily persuaded to exit quickly -- then your other threads may have to wait a long time, possibly forever, before they can join() that thread and terminate safely)

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • Asynchronous programming is not a way to manage threads. Please see https://stackoverflow.com/a/34681101/11683. – GSerg Oct 02 '21 at 22:55
  • Sure, but if you didn't have access to an asynchronous programming API, you could use threads to implement one. – Jeremy Friesner Oct 02 '21 at 23:04
0

The re-invigoration of asynchronous programming has little to do with OS/kernel architecture to date. OSes have blocked the way you describe since the 1960s; although the reason has changed somewhat.

In early systems, efficiency was measured by useful CPU cycles; so switching tasks when one was blocked was a natural act.

Modern systems architecture is frequently addressing how to keep the CPUs occupied; for example if there are 800 CPUs but 20 tasks; then 780 CPUs have nothing to do.

As a concrete example, a program to sum all the bytes of a file might look a bit like:

for (count=0;  (c = getchar()) != EOF; count += c) {}

A multi-threaded version, for performance increase might look like:

for (n=0; n < NCPU; n++) {
    if (threadfork() == 0) {
        offset_t o = n* (file_size / NCPU);
        offset_t l = (file_size / NCPU);
        for (count = 0; l-- && pread(fd, &c, 1, o) == 1; count += c) {}
        threadexit(count);
    }
}
for (n=0; n < NCPU; n++) {
    threadwait(&temp);
    total += temp;
}
return total;

which is a bit grim, both because it is complex, and probably has inconsistent speed-ups. In comparison the function:

int GetSum(char *data, int len) {
    int count = 0;
    while (len--) {
        count += *data++;
    }
    return count;
}

I could construct a sort of dispatcher which, when a lump of file data became available in ram, invoked GetSum() on it, queuing its return value for later accumulation. This dispatcher could invest in familiarity with optimal i/o patterns etc.. since it may be applicable to many problems; and the programmer has a considerably simpler job to do.

Even without that sort of native support; I could mmap(2) the file, then dispatch many threads to just touch a page, then invoke GetSum on that page. This would effectively emulate an asynchronous model in a plain old unix-y framework.

Of course nothing is quite that easy; even a progress bar in a dispatch-oriented asynchronous model is dubious at best (not that the 1950s- based sequential ones were anything to write home about ). Communicating errors is also cumbersome; and because you use asynch to direct maximum cpu resources at yourself, you need to minimize synchronization operations (duh, async :).

Async has a lot of possibilities; but it really needs languages with defacto async support, not as an aspirational nod from the latest du jour standard of some rickety language.

mevets
  • 10,070
  • 1
  • 21
  • 33