[Knowledge Popularization] Faster than multi-threading? Understand what is a coroutine

What is a thread

Thread is the smallest unit that the operating system can perform operation scheduling. In most cases, it is included in the process and is the actual operating unit in the process. A thread refers to a single sequential control flow in a process. Multiple threads can be concurrent in a process, and each thread executes different tasks in parallel.

In some systems, threads are also called lightweight processes. The lightweight process generally refers to the kernel thread.

A process can have many threads to handle, and each thread executes different tasks in parallel. If the process has a lot of tasks to complete, it requires a lot of threads and calls a lot of cores. Therefore, the use of multi-threaded programming on a multi-core CPU can make more efficient use of the CPU.

Multi-threaded example:

@Test
    public void testThread() {
        Thread thread1 = new Thread(() -> funA(),"Thread-1");
        Thread thread2 = new Thread(() -> funB(),"Thread-2");
        Thread thread3 = new Thread(() -> funC(),"Thread-3");

        thread1.start();
        thread2.start();
        thread3.start();
    }

    public void funA() {
        System.out.println("A1");
        System.out.println("A2");
        System.out.println("A3");
    }

    public void funB() {
        System.out.println("B1");
        System.out.println("B2");
        System.out.println("B3");
    }

    public void funC() {
        System.out.println("C1");
        System.out.println("C2");
        System.out.println("C3");
    }

operation result:

A1
C1
C2
C3
B1
A2
A3
B2
B3

Schematic diagram of thread scheduling:

image

Thread switching

As shown in the figure above, a thread is bound to a task, and the task will be executed in the CPU. In systems such as Windows and Linx, task scheduling uses a time slice preemption call method. The status of the task is divided into running status and ready status. For example, when task 1 reaches the ready state, it will perform interrupt processing and save the execution information related to the current task (stored in memory). The operating system kernel will assign which task can be executed next. We assume that it is assigned to Task 3 is executed. When task 3 is interrupted, it may jump to task 2, or it may jump back to task 1, read the saved execution information, and continue execution.

There will be a switch back and forth between 3 threads.

The thread blocking switch needs to switch on the kernel of the operating system, and there will be a switch from user mode to kernel mode.

Coroutines and threads

As described above, we know that the scheduling of tasks in threads is operated by the kernel, and there will be a leap from user space to kernel space, which is not conducive to system performance. And the smart programmer thought, why not write a set of programs to control the scheduling between tasks, so that the task scheduling is controlled in the user space.

Regarding the coroutine, let's take a look at the introduction on the coroutine on Wikipedia:

Coroutines are very similar to threads. But the coroutine is cooperative multitasking, and the thread is typically preemptive multitasking. This means that coroutines provide concurrency rather than parallelism. Compared with threads, coroutines have the advantage that they can be used in hard real-time contexts (the switching between coroutines does not involve any system calls or any blocking calls), and there is no need to guard the synchronization principle of key blocks. Language (primitive) such as mutexes, semaphores, etc., and does not require support from the operating system. It is possible to use preemptively scheduled threads to implement coroutines in a way that is transparent to the calling code, but some benefits will be lost (especially the suitability of hard real-time operations and relatively inexpensive switching between them).

A thread is a lightweight thread of cooperative multitasking, essentially describing the same concept as a coroutine. The difference, if it must be said, is that a coroutine is a language-level structure, which can be regarded as a form of control flow, while a thread is a system-level structure, which can be regarded as a thread that happens to not run in parallel. It is controversial whether these two concepts have priority: threads can be regarded as an implementation of a coroutine, or as a basis for the realization of a coroutine.

image

Process and thread

A thread actually executes a subroutine, so what is a subroutine?

Subroutines are functions, and methods in Java. If a funA calls funB, and funB calls funC, then funC needs to be executed first, then funB is executed, and finally funA is executed.

The internal call of the entire subroutine is implemented through the stack.

So what is the relationship between the coroutine and the subroutine?

In fact, a coroutine is also a subroutine, but during the execution of the subroutine, the inside of the subroutine can be interrupted to execute other subroutines, and then return to execute the current subroutine.

Is this a bit like a CPU interrupt?

Let's look at a piece of code:

funA() {
  println("A1");
  println("A2");
  println("A3");
}

funB() {
  println("B1");
  println("B2");
  println("B3");
}

funC() {
  println("C1");
  println("C2");
  println("C3");
}

If the above code is executed by the coroutine, it may be interrupted at some point internally when funA is executed, and then funB may be executed, or funC may be executed back. The final result may be:

A1
A2
C1
C3
A3
B3
B2
C2
B1

Note here that the three functions do not call each other. The display here does not use multi-threading technology, but only uses one thread for execution.

Since multi-threading can also achieve the above functions, why is the birth of a coroutine?

In the coroutine, the switching of subroutines is not the switching between threads, but is controlled by the program itself. So compared to multithreading, there is no overhead of switching back and forth between threads. Especially in the case of a large number of threads.

If we need to operate shared resources in multithreading, we need to use locks, we may use various levels of locks, and even use operating system-level locks. If it is in a coroutine, there is no need to use locks.

In general, the coroutine improves the efficiency of program execution. If our system is multi-core, we can use multi-core plus coroutine to maximize system performance.

About the development history of coroutines

The concept of coroutines was first born in the 1990s, and the concept of threads was only proposed in the 1980s. As far as usage is concerned, the use of coroutines is far less extensive than threads.

In recent years, some upstarts in programming languages, Go and Kotlin, have introduced the language feature of coroutines, which makes the concept of coroutines that seem to be very unfamiliar to come into everyone's field of vision frequently.

I believe that for programmers who are engaged in Java development, it is normal that they have not heard of coroutines. I also saw a Tencent interview guide on the Internet, and then I realized that there is a coroutine.

If the language you master is Go, Kotlin or Python, then you should learn about coroutines. Dachang prefers to interview these things.

Well, if someone asks you what a coroutine is, you can say: a coroutine is a lightweight thread.

reference

1. Coroutine-Wikipedia, the free encyclopedia (wikipedia.org)-https://zh.wikipedia.org/wiki/coroutine

2. Multithreading-Wikipedia, the free encyclopedia (wikipedia.org)-https://zh.wikipedia.org/wiki/Multithreading

3. Coroutine-Liao Xuefeng's official website (liaoxuefeng.com)-https://www.liaoxuefeng.com/wiki/897692888725344/923057403198272

 

Recommended in the past

Scan the QR code to get more exciting. Or search Lvshen_9 on WeChat , you can reply to get information in the background

Guess you like

Origin blog.csdn.net/wujialv/article/details/114694916