0

I learned that all of user threads mapped with a kernel thread be blocked if one of the threads calls some system call likes I/O System Call.

If std::thread is implemented by creating only a user thread in some environment, then a thread for I/O in some programs can block a thread for Rendering.

So I think distinguishing user / kernel is important but c++ standard does not.

Then how I assure that some situations like above will not occur in particular environment(like Windows10 )?

Mona04_
  • 56
  • 5
  • If the system doesn't have kernel threads, then you cannot assume that std::thread would use kernel threads. Otherwise it's reasonable to assume until you have any evidence of contrary. – eerorika Feb 04 '22 at 11:29
  • What do you mean by "kernel thread": a [kernel-space thread](http://v4l.videotechnology.com/dwg/kernelthreads/kernelthreads.html) or a user-space thread created using system calls? If you talk about the first, then I think that the STL clearly does not target such a use-case (and I would be surprised if an OS implement this). If you are talk about the second, AFAIK C++ guarantee the concurrency of the function calls. Thus, I do not thing the case you describe should ever happen. Actually, it would certainly break any code using non-recursive mutexes with a thread-dependent waiting code. – Jérôme Richard Feb 04 '22 at 11:34
  • Thanks. Now I learned that it is not implemented in that way as much as possible. – Mona04_ Feb 04 '22 at 11:53

3 Answers3

1

Rephrasing my attempt to answer, after talking to the OP and understanding better what is really being asked.

Most I/O operations are blocking per thread level: if a threads starts one, only this thread will be blocked, not the whole process.

The OP seems to intend to start a rendering operation in a thread and doesn't want it to be blocked by an I/O operation in this thread. Two possible solutions are:

  1. To spawn another thread to do this blocking I/O operation, and then let the rendering thread to proceed independently of the I/O;
  2. To use resources specific of each OS (that doesn't belong to C++), to start the same I/O operation in an asynchronous, non blocking form.
  3. Lastly, to minimize the blocking of the OS access to I/O, what an application developer can do is to try to make sure that there's no simultaneous access to the same I/O device at the same time.
Hilton Fernandes
  • 559
  • 6
  • 11
  • [Wiki](https://en.wikipedia.org/wiki/Thread_(computing)#User_threads) says that "... does not return until the I/O operation has been completed. In the intervening period, the entire process is "blocked" by the kernel and cannot run ...". I think your answer contradict with Wiki. Do I misunderstand? – Mona04_ Feb 04 '22 at 12:38
  • 1
    Please read with care the Wikipedia: a user thread will be blocked by a *blocking* I/O operation. Therefore, if you need to do your rendering and *also* a blocking I/O operation, there are two ways to do it: either you spawn another thread for your I/O op, or you start a non-blocking I/O operation, using the OS specific resources for that. Not all I/O is blocking, and only the thread that started one will be blocked, not the whole process. – Hilton Fernandes Feb 04 '22 at 12:49
  • My question is how can I know the newly spawned thread(std::thread) is not sharing the same kernel-space threads with a user thread that needs a blocking I/O operation. As I understand, your answer is not related with my question. – Mona04_ Feb 04 '22 at 12:57
  • 1
    I am sorry if couldn't understand what you asked. Can you please consider rephrasing your question ? That will help others answer it better. In any case, the answer to your new question form lies in my attempt to answer it: a thread will be blocked to access a device if another thread is already using it. – Hilton Fernandes Feb 04 '22 at 13:03
  • In other words, your application should make sure no two independent threads try to access the same device at the same time. – Hilton Fernandes Feb 04 '22 at 13:04
1

I learned that all of user threads mapped with a kernel thread be blocked if one of the threads calls some system call likes I/O System Call.

Yes, however it's rare for anything to use kernel's system calls directly. Typically they use a user-space library. For a normally blocking "system" call (e.g. the read() function in a standard C library) the library can emulate it using asynchronous functions (e.g. the aio_read() function in a standard C library) and a user-space thread switches.

So I think distinguishing user / kernel is important but c++ standard does not.

It is important, but for a different reason.

The first problem with user-space threading is that the kernel isn't aware of thread priorities. If you imagine a computer running 2 completely separate applications (with the user using "alt+tab" to switch between them), where each application has a high priority thread (for user interface), and few medium priority threads (for general work) plus a few low priority threads (for doing things like prefetching and pre-calculating stuff in the background); you can end up with a situation where kernel gives CPU time to one application (that uses the CPU time for low priority threads) because it doesn't know the other application needs CPU time for its higher priority threads.

In other words, for a multi-process environment, user-space threading has a high risk of wasting CPU time doing irrelevant work (in one process) while important work (in another process) waits.

The second problem with user-space threading is that (for modern systems) good scheduling decisions take into account differences between different CPUs ("big.Little", hyper-threading, which caches are shared by which CPUs, ..) and power management (e.g. for low priority threads it's reasonable to reduce CPU clock speed to increase battery life and/or reduce CPU temperatures so they can run faster for longer when higher priority work needs to be done later); and user-space has none of the information needed (and none of the ability to change CPU speeds, etc) and can not make good scheduling decisions.

Note that these problems could be "fixed" by having a huge amount of communication between user-space and kernel (the user-space threading informing kernel of thread priorities of waiting threads and currently running thread, kernel informing user-space of CPU differences and power management, etc); but the whole point of user-space thread switching is to avoid the cost of kernel system calls, so this communication between user-space and kernel would make user-space thread switching pointless.

Then how I assure that some situations like above will not occur in particular environment(like Windows10 )?

You can't. It's not your decision.

When you choose to use high level abstractions (std::thread in C++ rather than using the kernel directly from assembly language) you are deliberately delegating low level decisions to something else (the compiler and its run-time environment). The advantages (you no longer have to care about these decisions) are the disadvantages (you are no longer able to make these decisions).

Brendan
  • 35,656
  • 2
  • 39
  • 66
  • Then, user-level threads in modern operate system might be not used as there are few benefits. So I can expect user-level threads is not used as long as I use high level language like c++. Is it right? – Mona04_ Feb 04 '22 at 14:53
  • 1
    @Mona04_: I'd be surprised if there was an implementation of C++ for modern operating systems that uses user-level threads. Normally the C++ `std::thread` stuff is a thin wrapper over what the OS/kernel provides. Sadly this also means that `std::thread` is relatively worthless on its own (e.g. see https://stackoverflow.com/questions/18884510/portable-way-of-setting-stdthread-priority-in-c11 ) – Brendan Feb 04 '22 at 20:35
0

You can be assured that std::thread is not using "user threads" because that concept pretty much died around the turn of the century.

Modern hardware has multiple CPU cores, which work much better if there are sufficient kernel threads. Without enough kernel threads, CPU cores may sit idle.

The idea of "user threads" originated in an era when there was only a single CPU core, and people instead worried about having too many kernel threads.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Then until kernel-thread is not available, ```std::thread``` do like [One-To-One Models](https://docs.oracle.com/cd/E19620-01/805-4031/6j3qv1oej/index.html)? – Mona04_ Feb 04 '22 at 13:51
  • "User threads" like fibers are still used and useful in many situations. They can be used to improve performance of [AAA games](https://www.gdcvault.com/play/1022186/Parallelizing-the-Naughty-Dog-Engine), to implement [efficient asynchronous feature](https://greenlet.readthedocs.io/en/latest/) (the same applies also for C# and FORTRAN AFAIK), or for interesting concurrency features (see Erlang & Go). In fact, C++20 coroutines could be implemented using fibers. Fibers are available in many languages including quite recent ones like D, Ruby and Julia. So, no, the concept is not dead. – Jérôme Richard Feb 04 '22 at 18:11
  • I'd say the idea of "user threads" originated from old operating systems. Before threads existed operating systems (e.g. Unix) only scheduled whole (single-threaded) processes, and doing threads in user-space was a way to make threads work on these old systems. Even newer operating systems (Linux) used user-space threads for ages (until they finally implemented kernel space threads). Later (1990s?) it switched to being a performance thing (avoiding the cost of kernel system calls). Note that (for multi-CPU) there's a very old "M user-space threads mapped to N kernel-space threads" approach. – Brendan Feb 04 '22 at 20:54