Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion regarding the Blocking of "peer threads" when a user-level thread blocks

I was reading about differences between threads and processes, and literally everywhere online, one difference is commonly written without much explanation:

If a process gets blocked, remaining processes can continue execution. If a user level thread gets blocked, all of its peer threads also get blocked.

It doesn't make any sense to me. What would be the sense of concurrency if a scheduler cannot switch between a blocked thread and a ready/runnable thread. The reason given is that since the OS doesn't differentiate between the various threads of a given parent process, it blocks all of them at once.

I find it very unconvincing, since all modern OS have thread control blocks with a thread ID, even if it is valid only within the memory space of the parent process. Like the example given in Galvin's Operating Systems book, I wouldn't want the thread which is handling my typing to be blocked if the spell checking thread cannot connect to some online dictionary, perhaps.

Either I am understanding this concept wrong, or all these websites have just copied some old thread differences over the years. Moreover, I cannot find this statement in books, like Galvin's or maybe in William Stalling's COA book where threads have been discussed.

These are resouces where I found the statements:

  • https://www.geeksforgeeks.org/difference-between-process-and-thread/
  • https://www.tutorialspoint.com/difference-between-process-and-thread
  • https://www.guru99.com/difference-between-process-and-thread.html
  • https://www.javatpoint.com/process-vs-thread
like image 844
AMAN KUMAR Avatar asked Aug 30 '21 09:08

AMAN KUMAR


People also ask

Why if one user-level thread perform blocking operation then entire process will be blocked?

Many to One Model Thread management is done in user space by the thread library. When thread makes a blocking system call, the entire process will be blocked. Only one thread can access the Kernel at a time, so multiple threads are unable to run in parallel on multiprocessors.

What is the user-level thread blocking problem?

One major problem with user-level threads is the blocking of all threads within a process when one blocks. A possible solution is known as jacketing. A blocking system call has a user-level jacket. The jacket checks to see if the resource is available, e.g., device is free.

When a user-level thread of a process makes a system call that leads to blocking all threads of the process become blocked?

When any user level thread makes a blocking call, the process as a whole becomes blocked. User level threads allow multiple blocking function calls to be made in parallel. In other words, while a user level thread is blocked, other user level threads can make their own blocking function calls.

Does blocking one thread blocks all related threads?

Option (D): Blocking one kernel level thread blocks all related threads. false, since kernel level threads are managed by operating system, if one thread blocks, it does not cause all threads or entire process to block.


3 Answers

There is a difference between kernel-level and user-level threads. In simple words:

  • Kernel-level threads: Threads that are managed by the operating system, including scheduling. They are what is executed on the processor. That's what probably most of us think of threads.
  • User-level threads: Threads that are managed by the program itself. They are also called fibers or coroutines in some contexts. In contrast to kernel-level threads, they need to "yield the execution", i.e. switching from one user-level to another user-level thread is done explicitly by the program. User-level threads are mapped to kernel-level threads.

As user-level threads need to be mapped to kernel-level threads, you need to choose a suiteable mapping. You could map each user-level to a separate kernel-level thread. You could also map many user-level to one kernel-level thread. In the latter mapping, you let multiple concurrent execution paths be executed by a single thread "as we know it". If one of those paths blocks, recall that user-level threads need to yield the execution, then the executing (kernel-level) thread blocks, which causes all other assigned paths to also be effectively blocked. I think, this is what the statement refers to. FYI: In Java, user-level threads – the multithreading you do in your programs – are mapped to kernel-level threads by the JVM, i.e. the runtime system.


Related stuff:

  • Understanding java's native threads and the jvm
  • What is the difference between a thread and a fiber?
  • What is difference between User space and Kernel space?
  • What is the difference between concurrent programming and parallel programming?
  • Implementing threads
like image 53
akuzminykh Avatar answered Oct 22 '22 02:10

akuzminykh


Back in the early days of Java at least, user-level threads were called "green threads", to implement Java threading on OSes that didn't support native threading. There's still a Wiki article https://en.wikipedia.org/wiki/Green_threads which explains the origin and meaning.

(This was back when desktops/laptops were uniprocessor systems, with a single-core CPU in their 1 physical socket, and SMP machines mostly only existed as multi-socket.)

You're right, this was terrible, and once mainstream OSes grew up to support native threads, people mostly stopped ever doing this. For Java specifically at least, Green threads refers to the name of the original thread library for the programming language Java (that was released in version 1.1 and then Green threads were abandoned in version 1.3 to native threads).

So use Java version 1.3 or later if you don't want your spell-check thread to block your whole application. :P This is ancient history.

Although there is some scope for using non-blocking IO and context switching when a system call returns that it would block, but usually it's better to let the kernel handle threads blocking and unblocking, so that's what normal modern systems do.


IIRC on Solaris there was also some use of an N:M model where N user-space threads might be handled by fewer than N kernel threads. This could mean having some "peer" threads (that share the same kernel thread) like in your quote without being fully terrible purely userspace green threads.

(i.e. only some of your total threads are sharing the same kernel thread.)

pthreads on Linux uses a 1:1 model where every software thread is a separate task for the kernel to schedule.

Google found https://flylib.com/books/en/3.19.1.51/1/ which defines those thread models and talks about them some, including the N:M hybrid model, and the N:1 user-space aka green threads model that needs to use non-blocking I/O if it wants to avoid blocking other threads. (e.g. do a user-space context switch if a system call returns EAGAIN or after queueing an async read or write.)

like image 2
Peter Cordes Avatar answered Oct 22 '22 02:10

Peter Cordes


Okay, the other answers provide detailed information.

But to hit your main convern right in the middle:

  • the article is putting that a bit wrong, lacking the necessary context (see all the details in @akuzminykh 's explanation of user-level threads and kernel-level threads)
  • what this means for a Java programmer: don't bother with those explanations. If one of your Java threads blocks (due to I/O etc), that will have NO IMPACT on any other of your threads (unless, of course, you explicitly WANT them to, but then you'd have to explicitly use mechanisms for that)

How do Threads get blocked in Java?

  • If you call sleep() or wait() etc, the Thread that currently executes that code (NOT the objects you call them on) will be blocked. These will get released on certain events: sleep will finish once the timer runs out or the thread gets interrupted by another, wait will release once it gets notified by another thread.
  • if you run into a synchronized(lockObj) block or method: this will release once the other thread occupying that lockObj releases it
    • closely related to that, if you enter ThreadGates, mutexes etc, all those 1000s of specialized classes for extended thread control like rendezvous etc
  • If you call a blocking I/O method, like block reading from InputStream etc: int amountOfBytesRead = read(buffer, offset, length), or String line = myBufferedReader.readLine();
    • opposed to that, there are many non-blocking I/O operations, like most of the java.nio (non-blocking I/O) package, that return immediately, but may indicate invalid result values
  • If the Garbage Collector does a quick cleanup cycle (which are usually so short you will not even notice, and the Threads get released automatically again)
  • if you call .parallelStream() functions for certain long-lasting lambda functions on streams (like myList.parallelStream().forEach(myConsumerAction)) that - if too complex or with too many elements - get handled by automated multithreading mechanisms (which you will not notice, because after the whole stuff is done, your calling thread will resume normally, just as if a normal method was called). See more here: https://www.baeldung.com/java-when-to-use-parallel-stream
like image 2
JayC667 Avatar answered Oct 22 '22 02:10

JayC667