preface

As we all know, 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 the scheduling of tasks, the allocation and management of resources, and governs the entire computer hardware. An application is a functional program that runs on an operating system.

A, processes,

A process is a dynamic execution process of a program with certain independent functions on a data set. It is an independent unit of the operating system to allocate and schedule resources, and a carrier of application program running. A process is an abstract concept, and there has never been a standard definition.

Process composition

A process generally consists of three parts: program, data set and process control block.

  • Program is used to describe the process to complete the function, is to control the process execution instruction set;
  • The data set is the data and workspace that the program needs to execute;
  • A Program Control Block (PCB for short) contains the description and Control information of a process and is the only symbol of the existence of a process.

Characteristics of processes:

  • Dynamic: process is an execution process of the program, is temporary, has a life period, is dynamic generation, 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: a process consists of three parts: program, data and process control block.

Second, the thread

In early operating systems, there was no concept of threads. A process was the smallest unit that could hold resources and run independently. It was also the smallest unit of program execution. Task scheduling adopts the preemptive scheduling mode of time slice rotation, and the process is the smallest unit of task scheduling. Each process has its own independent piece of memory, which makes the memory addresses of each process isolated from each other.

Later, with the development of computers, the requirements for CPU are more and more high, and the switching between processes is more expensive, which can not meet the requirements of more and more complex programs. Hence the invention of threads.

Thread is a single sequence control flow in program execution, 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 program’s memory space (that is, the memory space of the process). A standard thread consists of the thread ID, the current instruction pointer (PC), the register, and the stack. A process consists of a memory space (code, data, process space, open files) and one or more threads. (This may confuse some readers and make them feel that this is not quite the same as the Java memory space model, but if you have read the Java Virtual Machine in depth, you will understand.)

As shown in the figure above, in the process column of task manager, Youdao Dictionary and Youdao Cloud note are processes, and there are multiple threads executing different tasks under the process.

Task scheduling

What is a thread? To understand this concept, you need to understand some concepts related to operating systems. Task scheduling in most operating systems (such as Windows and Linux) adopts preemptive scheduling based on 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 execution of the thread resumes to proceed to the next task. In this process, the short period of time that the task executes is called the time slice, the state when the task is executing is called the running state, and the suspended thread task state is called the ready state, which means it is waiting for the next time slice that belongs to it.

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: Execute multiple tasks simultaneously). The schematic diagram of the multi-task running process is as follows:

Task scheduling in the operating system

The difference between process and thread

We talked about processes and threads earlier, but you might be confused about how similar they are. Sure, processes and threads have a lot to do with each other, so let’s get to the bottom of it:

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

Schematic diagram of the relationship between threads and processes:

Resource sharing between processes and threads

Fourth, the relationship between single thread and multi-thread

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

Early operating systems had processes, not threads

Later, as computers developed, the need for more efficient context switching between multiple tasks led to the abstraction of a smaller concept, the thread. A process typically has more than one thread.

The emergence of threads makes it possible for a process to have multiple threads

5. Multithreading and multi-core

The aforementioned time-slicing mode of scheduling says that a task is suspended after a short period of time to execute the next task, and each task is executed in turn. Many operating system books say that “only one task is performing at a time.” What about dual-core processors, one might ask? Aren’t both cores running at the same time?

The phrase “only one task at a time” is not accurate, at least it is not comprehensive. How do threads execute in the case of multi-core processors? This requires an understanding of kernel threads.

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

Today’s computer is generally dual-core four threads, four-core eight threads, is the use of hyper-threading technology to simulate a physical processing core into two logical processing cores, corresponding to two kernel threads, so in the operating system to see the number of CPU is the actual number of physical CPU twice, if your computer is dual-core four threads, Open task Manager performance to see the monitor for 4 cpus, and quad core 8 threads to see the monitor for 8 cpus.

Dual-core four-thread view results under Windows8

Hyperthreading technology is the use of special hardware instructions, a physical chip is simulated into two logic processing core, so that a single processor can use thread level parallel computing, and then compatible with multithreaded operating systems and software, reduce the idle time of the CPU, improve the CPU running efficiency. Such hyperthreading techniques (such as dual-core four-thread) are determined by the processor hardware and require operating system support to be implemented in the computer.

Programs do not use kernel threads directly. Instead, they use a high-level interface of kernel threads called Lightweight Process (LWP), which is what we call a thread in general, also known as a user thread. Since each lightweight process is supported by a kernel thread, a lightweight process can only exist if the kernel thread is supported first. There are three models for the relationship between user threads and kernel threads: one-to-one model, many-to-one model, and many-to-many model. In this paper, four kernel threads and three user threads are taken as examples to illustrate the three models.

Vi. 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). In this way, if the CPU does not use hyper-threading technology (such as a four-core, four-thread computer), a user thread is uniquely mapped to the core 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 blocks 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 has two disadvantages:

  1. Many operating systems limit the number of kernel threads, so the one-to-one model limits the number of user threads;
  2. In many operating system kernel thread scheduling, context switching is costly, resulting in the execution efficiency of user threads.

One-to-one model

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, so the system kernel does not feel the implementation of threads. User threads are created, synchronized, and destroyed in user mode without kernel intervention. Therefore, compared with the one-to-one model, the many-to-one thread context switch is much faster. In addition, the many-to-one model has an almost unlimited number of user threads.

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

  1. If one of the user threads blocks, then none of the other threads can execute because the kernel thread is also blocked.
  2. On a multiprocessor system, increasing the number of processors does not significantly improve thread performance in the many-to-one model because all user threads are mapped to a single processor.

Many-to-one model

Many-to-many model

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

The advantages of many-to-many model are as follows:

  1. A user thread blocking does not cause all threads to block, because there are other kernel threads scheduled to execute.
  2. The many-to-many model has no limit on the number of user threads;
  3. In multiprocessor operating systems, the performance of threads in many-to-many model can also be improved, but not as much as that in one-to-one model.

Many-to-many model

In the current popular operating systems, most of the use of the many-to-many model.

View processes and threads

An application may be multithreaded or multi-process. How do you view it? In Windows we can simply open task Manager to see the number of processes and threads in an application. Press Ctrl+Alt+Del or the right shortcut 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 each process, as in the image above, Google’s Chrome browser has multiple processes. At the same time, if you have multiple instances of an application open, you will also have multiple processes, such as in the image above where I have two CMD Windows open, there are two CMD processes. If you do not see the thread count column, you can click the “View column” menu again and add the column to listen to. View CPU and memory usage: In the performance TAB, you can view CPU and memory usage and the number of logical processing cores based on the number of monitors that record CPU usage, such as my dual-core, four-thread computer.

Check the CPU and memory usage

Thread life cycle

When the number of threads is less 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, the concurrency of threads is somewhat hampered and is not truly concurrent because at least one processor is running multiple threads.

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

We know that a thread is the smallest unit of program execution, and also the smallest unit of task execution. In earlier process-only operating systems, processes had five states: created, ready, running, blocked (waiting), and exit. The life cycle of a multithread is similar to that of an earlier process, which now has only one thread.

The life cycle of an early process

A process has three states when it is running: ready, running, blocked, created, and exited. The states describe the creation and exit of a process.

  • Create: The process is being created and cannot run yet. When the operating system is creating the process, the work includes the allocation and establishment of process control block entries, the establishment of resource tables and allocation of resources, the loading of programs and the establishment of address space;

  • Ready: The time slice has been used up, and the thread is forced to pause to wait for the next time slice of its own.

  • Running: This thread is executing and occupying the time slice.

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

  • Exit: The process has ended, so it is also called the end state, releasing the resources allocated by the operating system.

The life cycle of the thread

  • Create: a new thread is created, waiting for the thread to be called to execute;

  • Ready: The time slice has been used up, and the thread is forced to pause to wait for the next time slice of its own.

  • Running: This thread is executing and occupying the time slice.

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

  • Exit: When a thread completes a task or another termination condition occurs, the thread terminates and enters an exit state, which releases resources allocated to the thread.

Ten, coroutines

Coroutines, or Coroutines, are based on threads, but they are lighter than threads. These lightweight threads are managed by programmers who write their own programs. They are called “user-space threads” and have features that are invisible to the kernel.

Many people also prefer to call them fibers, or greenthreads, because they are autonomous asynchronous tasks. Just as a process can have multiple threads, a thread can have multiple coroutines.

The purpose of the coroutine

In traditional J2EE systems, it takes one thread per request to complete the business logic (including transactions). So the throughput of the system depends on the operation time of each thread. If the I/O behavior is very time-consuming, the throughput of the entire system immediately drops because the thread is always blocked. If there are many threads, many threads will be idle (waiting for the thread to finish executing), resulting in incomplete application of the resource.

The most common example is JDBC (which is synchronously blocked), which is why many people talk about the database as a bottleneck. In this case, the CPU is waiting for the I/O to return. In other words, the thread is idling instead of using the CPU to do any computation. In addition, too many threads will bring more ContextSwitch overhead.

One of the most popular solutions in the industry today is single threading with asynchronous callbacks. This is represented by Node.js and the new vert. x in Java.

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

Features of coroutines

  1. Thread switches are scheduled by the operating system, and coroutines are scheduled by the users themselves, thus reducing context switches and improving efficiency.
  2. The default Stack size for threads is 1M, while coroutines are lighter, approaching 1K. So you can turn on more coroutines in the same memory.
  3. Because you are on the same thread, you can avoid contention and use locks.
  4. For blocked scenarios that require a lot of concurrency. However, it is not suitable for multithreading of a large number of calculations. In this case, it is better to use threads to solve the problem.

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, The entire process can be called a Coroutine, and the threads running in the coroutine are called fibers. For example, the go keyword in Golang is responsible for opening a Fiber and running func logic on it.

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

Coroutines versus threads

This is the end of the article

Like xiaobian to share technical articles can be like attention oh!

Small make up here some Java core knowledge points hundreds of pages of information collection

Pay attention to the public number: Kirin to change the bug