preface

We all know that the core of a computer is the CPU, which undertakes all the computing tasks; The operating system is the manager of the computer, it is responsible for task scheduling, resource allocation and management, command the entire computer hardware; An application is a program that has some function and runs on the operating system.

A, processes,

Process is a dynamic execution process of a program with certain independent functions in a data set. It is an independent unit of the operating system for resource allocation and scheduling. It is the carrier of application program operation. Process is an abstract concept and there has never been a unified standard definition.

Process composition

Process is generally composed of three parts: program, data set and process control block.

  • Program is used to describe the process to complete the function, is the control process execution instruction set;
  • The data set is the data and workspace required by the program during execution.
  • Program Control Block (PCB), which contains the description and Control information of a process, is the unique symbol of the existence of a process.

Characteristics of a process:

  • Dynamic: process is an execution process of the program, which is temporary and has a life span, dynamic generation and dynamic extinction.
  • Concurrency: Any process can execute concurrently with other processes.
  • Independence: a process is an independent unit of the system for resource allocation and scheduling.
  • Structure: process consists of program, data and process control block.

Second, the thread

In early operating systems, there was no concept of a thread. A process was the smallest unit that could own resources and run independently, and the smallest unit of program execution. Task scheduling adopts the preemptive scheduling method of time slice rotation, and process is the smallest unit of task scheduling. Each process has its own independent piece of memory, so that the memory address of each process is isolated from each other.

Later, with the development of the computer, the CPU requirements are higher and higher, the switching overhead between processes is larger, has been unable to meet the requirements of more and more complex programs. So we invented threads.

Thread is a single sequence control flow in program execution. It is the smallest unit of program execution flow and the basic unit of processor scheduling and dispatching. A process can have one or more threads that share the memory space of the program (that is, the memory space of the process in which it is running). A standard thread consists of a thread ID, the current instruction pointer (PC), registers, and a stack. A process consists of memory space (code, data, process space, open files) and one or more threads. (This may be confusing to some readers, who feel that this is not quite the same as the Java memory space model, but if you read the book on understanding the Java Virtual Machine in depth, you will understand.)

As shown above, in the process column of Task Manager, Youdao Dictionary and Youdao Cloud Note are processes, under which there are multiple threads performing different tasks.

Task scheduling

What is a thread? To understand this concept, you need to understand some concepts related to operating systems. Most operating systems (such as Windows and Linux) use the preemptive scheduling method of time slice rotation.

In a process, when a thread task executes a few milliseconds later, will be by the operating system kernel is responsible for managing the task schedule, through hardware counter interrupt handler, let this thread forced to pause and put the thread registers in memory, by looking at the thread list to decide which one the next execution threads, and restore the thread from memory registers, Finally, the thread resumes to execute the next task. In the above process, the short period of task execution is called time slice, the state when the task is executing is called running state, and the state of the suspended thread task is called ready state, which means waiting for the arrival of its next time slice.

This way in turn ensures that each thread execution, as a result of the CPU execution efficiency is very high, time is very short, to quickly switch between the various tasks, in the sense that gives a person is more tasks “to” at the same time, this also is what we call the concurrent (don’t think how advanced concurrency, its implementation is very complex, but the concept is very simple, is a word: Multiple tasks simultaneously). The multi-task operation process is shown as follows:

Task scheduling in the operating system

Third, the difference between process and thread

I talked about processes and threads, but you might be confused by how similar they are. Processes and threads have a lot to do with each other. Let’s take a look at them:

  1. Thread is the smallest unit of program execution, and process is the smallest unit of resources allocated by the operating system;
  2. A process consists of one or more threads, threads are different lines of code execution in a process;
  3. Processes are independent from each other, but each thread under the same process shares the program’s memory space (including code segments, data sets, heap, etc.) and some process-level resources (such as open files and signals). Threads in one process are invisible to other processes.
  4. Scheduling and switching: Thread context switching is much faster than process context switching.

Diagram of thread and process relationship:

Resource sharing relationship between process and thread

The relationship between single thread and multi-thread

In short, threads and processes are both abstractions. Threads are a smaller abstraction than processes, and both can be used to achieve concurrency. In early operating systems, there was no concept of a thread. A process was the smallest unit that could own resources and run independently, and the smallest unit of program execution. It is equivalent to only one thread in a process, the process itself is the thread. So threads are sometimes called Lightweight processes (LWP).

Early operating systems had processes, not threads

Later, as computers became more efficient at switching contexts between multiple tasks, a smaller concept was abstracted out — threads, which typically have multiple (but one) threads per process.

With the advent of threads, a process can have multiple threads

Five, multithreading and multi-core

The time-slice rotation schedule mentioned above says that a task is executed for a short period of time and then forced to pause for the next task, each task is executed in turn. Many operating system books say that “only one task is executing at a time.” One might ask what about dual-core processors? Don’t the two cores run simultaneously?

The phrase “only one task at a time” is inaccurate, or at least incomplete. How does a thread execute in the case of a multi-core processor? This requires an understanding of kernel threads.

Multi-core (core) processors are the integration of multiple computing cores on a single processor to improve computing power, that is, there are multiple truly parallel computing processing cores, each corresponding to a kernel thread. Kernel threads (KLT) are threads directly supported by the operating system Kernel. This Thread is switched by the Kernel, which schedules the threads through the operation scheduler and maps the tasks of the threads to each processor. Generally, one processing core corresponds to one kernel thread. For example, a single-core processor corresponds to one kernel thread, a dual-core processor to two kernel threads, and a quad-core processor to four kernel threads.

Now the computer is generally dual-core four threads, four cores eight threads, is the use of hyperthreading technology to simulate a physical processing core into two logical processing cores, corresponding to two kernel threads, so the number of CPU seen in the operating system is twice the number of actual physical CPU, such as your computer is dual-core four threads, Open “Task Manager \ Performance” to see 4 CPU monitors, four core eight threads to see 8 CPU monitors.

Dual core four threads in Windows8 view the results

Hyperthreading technology is to use special hardware instructions to simulate a physical chip into two logic processing cores, so that a single processor can use thread-level parallel computing, and then compatible with multi-threaded operating system and software, reduce the IDLE time of CPU, improve the efficiency of CPU operation. This hyper-threading technique (such as dual-core quad threading) is dictated by the processor hardware and requires the support of the operating system to manifest itself in the computer.

Applications do not use kernel threads directly, but rather use a high-level interface of kernel threads called Lightweight processes (LWP). Lightweight processes are generally referred to as threads, also known as user threads. Since each lightweight process is supported by a kernel thread, there can be no lightweight process until kernel threads are supported first. There are three models for the corresponding relationship between user threads and kernel threads: one-to-one model, many-to-one model and many-to-many model. Here, four kernel threads and three user threads are taken as examples to illustrate the three models.

Six, one-to-one model

For the one-to-one model, a user thread uniquely corresponds to a kernel thread (the reverse is not necessarily true; a kernel thread does not necessarily have a corresponding user thread). Thus, if the CPU does not employ hyperthreading technology (such as a four-core, four-threaded computer), a user thread uniquely maps to the kernel thread of a physical CPU, and the concurrency between threads is true concurrency. The one-to-one model gives user threads the same advantages as kernel threads. If one thread is blocked for some reason, the execution of other threads is not affected. Here, too, the one-to-one model allows multithreaded programs to perform better on multiprocessor systems.

But the one-to-one model also has two drawbacks:

  1. Many operating systems limit the number of kernel threads, so a one-to-one model limits the number of user threads;
  2. In many operating system kernel thread scheduling, the overhead of context switch is high, which leads to the execution efficiency of user thread.

One-to-one model

7. Many-to-one model

The many-to-one model maps multiple user threads to a kernel thread, and the switch between threads is carried out by user-mode code, and the system kernel does not feel the implementation of threads. The creation, synchronization and destruction of user threads are all completed in user mode without the intervention of the kernel. Therefore, compared with one-to-one model, the many-to-one model has a much faster thread context switching speed. In addition, the many-to-one model has an almost unlimited number of user threads.

But the many-to-one model also has two drawbacks:

  1. If one of the user threads blocks, all the other threads cannot execute because the kernel thread is blocked.
  2. On a multiprocessor system, an increase in the number of processors does not significantly increase the threading performance of the many-to-one model because all user threads are mapped to one processor.

Many-to-one model

Many-to-many model

The many-to-many model combines the advantages of the one-to-one and many-to-one models to map multiple user threads to multiple kernel threads. The thread library is responsible for scheduling user threads on available schedulable entities, which makes context switching of threads very fast because it avoids system calls. But it increases complexity and the possibility of priority inversions, as well as suboptimal scheduling without extensive (and costly) coordination between user-mode and kernel schedulers.

The advantages of the many-to-many model are:

  1. The blocking of one user thread does not cause all threads to block, because other kernel threads are scheduled to execute.
  2. The many-to-many model has no limit on the number of user threads;
  3. In a multiprocessor operating system, many-to-many threads can also get some performance improvement, but the improvement is not as high as that of the one-to-one model.

Many-to-many model

In today’s popular operating systems, most use the many-to-many model.

View processes and threads

An application can be multithreaded or multiprocessed. how do I check? On Windows we can view the number of processes and threads in an application simply by opening the task Manager. Press Ctrl+Alt+Del or right-click the toolbar to open the Task Manager.

Check the number of processes and threads:

View the number of threads and processes

Under the Processes TAB, we can see the number of threads an application contains. If an application has multiple processes, we can see that each process, as in the image above, has multiple processes in Google’s Chrome browser. Also, if you open multiple instances of an application, there will be multiple processes. As shown in the figure above, when I open two CMD Windows, there will be two CMD processes. If you can’t see the thread count column, you can click the “View \ Select Columns” menu again to add listening columns. View CPU and memory usage: In the performance TAB, we can view CPU and memory usage, as well as the number of logical processing cores based on the number of monitors recorded by CPU usage. For example, my dual-core, four-threaded computer has four monitors.

Check the CPU and memory usage

The life cycle of threads

When the number of threads is smaller than the number of processors, the concurrency of threads is true concurrency, with different threads running on different processors. However, when the number of threads is greater than the number of processors, thread concurrency is somewhat hindered, which is not true concurrency because at least one processor is running multiple threads.

Concurrency is an simulated state when a single processor is running multiple threads. The operating system takes turns executing each thread in a time-slice rotation. Today, almost all modern operating systems, such as Unix, Linux, Windows, and macOS, use the preemptive scheduling method of time slice rotation.

We know that a thread is the smallest unit of program execution, and the smallest unit of task execution. In the early process-only operating systems, processes had five states: created, ready, running, blocked (waiting), and quit. The earlier process was equivalent to today’s single-thread process, so today’s multithreading has five states, and the life cycle of multithreading is similar to that of the earlier process.

The life cycle of an earlier process

A process can be in three states: ready, running, and blocked. The created and exit states describe the creation and exit of a process.

  • Created: The process is being created and cannot run yet. When the operating system creates a process, the work to be carried out includes allocating and establishing process control block table entries, establishing resource tables and allocating resources, loading programs and establishing address space;

  • Ready: The thread is forced to pause for the next slice when the slice is used up.

  • Running: This thread is executing, occupying the time slice;

  • Blocking: Also called a wait state, waiting for an event (such as an IO or another thread) to complete;

  • Exit: The process is terminated. Resources allocated by the operating system are released.

The life cycle of a thread

  • Create: A new thread is created and waits for it to be called to execute.

  • Ready: The thread is forced to pause for the next slice when the slice is used up.

  • Running: This thread is executing, occupying the time slice;

  • Blocking: Also called a wait state, waiting for an event (such as an IO or another thread) to complete;

  • Exit: when a thread completes a task or other termination condition occurs, the thread terminates and enters the exit state, which releases the resources allocated by the thread.

Ten, coroutines

Coroutines, or “Coroutines”, are threads based on threads, but lighter than threads. These lightweight threads managed by programmers themselves are called “user-space threads” and have features that are invisible to the kernel.

Because they are self-initiated asynchronous tasks, many people also prefer to call them fibers, or greenthreads. Just as a process can have multiple threads, a thread can have multiple coroutines.

The purpose of coroutines

Traditional J2EE systems are based on one thread per request to complete the entire business logic (including transactions). So the throughput of the system depends on the operation time of each thread. If a very time-consuming I/O is encountered, the throughput of the entire system immediately decreases, because the thread is always blocked. If there are many threads, there are many idle threads (waiting for the thread to finish executing), resulting in incomplete resource utilization.

The most common example is JDBC (which blocks synchronously), which is why many people talk about databases as bottlenecks. In this case, the CPU is waiting for the I/O to return, which means that the thread is idle instead of using the CPU to perform any computation. On the other hand, too many threads will incur more ContextSwitch overhead.

One of the most popular solutions to this problem in the industry is single-threaded with asynchronous callbacks. It’s represented by Node.js and the upstart Vert.x in Java.

The purpose of coroutines is to eliminate the ContextSwitch overhead when a long I/O operation occurs by giving up the current coroutine schedule to execute the next task.

Characteristics of coroutines

  1. Thread switching is scheduled by the operating system, while the coroutine is scheduled by the user himself, thus reducing context switching and improving efficiency.
  2. The default Stack size for threads is 1M, while coroutines are much lighter, approaching 1K. Therefore, more coroutines can be turned on in the same memory.
  3. Because you are on the same thread, you can avoid contention and use locks.
  4. This applies to blocked scenarios where large amounts of concurrency are required. However, it is not suitable for multithreading with a large number of computations. In this case, it is better to use threads to solve it.

The principle of coroutines

When in IO blocked by coroutines scheduler schedule, through the data flow yield away immediately (volunteer), and records the data on the current stack, immediately after the blocking by thread stack, and put the result of the block in this thread to go up and running, it looks like write synchronization code without any difference, This entire process can be called a Coroutine, and the threads running on it are called Fiber. For example, the go keyword in Golang is actually responsible for opening up a Fiber and letting funC logic run on it.

Because the pause of coroutine is completely controlled by program, it occurs in user state. The blocking state of the thread is switched by the operating system kernel, which occurs in the kernel state. Therefore, the overhead of the coroutine is much less than the overhead of the thread, and there is no overhead on the ContextSwitch.

Coroutines versus threads

That’s the end of the article

Like xiaobian to share technical articles can like attention oh!

Xiaobian here sorted out some Java core knowledge points hundreds of pages of information collection

Concern public number: Kylin bug