Concurrent programming Goroutine in Go language

Processes and threads

  • Process: A process is an execution process of a program in the operating system, an independent unit of the system for resource allocation and scheduling.
  • Thread: A thread is an execution entity of a process and the basic unit of CPU scheduling and dispatch. It is a basic unit that is smaller than a process and can execute independently.
  • The relationship between the two: a process can create and cancel multiple threads, and multiple threads in the same process can execute concurrently.

Coroutines in Go

  • Coroutine: independent stack space, shared heap space, scheduling is controlled by the user, similar to user-level threads.
  • Relationship with threads: A thread can create multiple coroutines, and coroutines are lightweight threads.

Goroutine in Go

When we want to implement concurrent programming in java/c++, we usually need to maintain a thread pool by ourselves, and we need to package tasks one after another, and we need to schedule threads to perform tasks and maintain context switching. All this usually Consume a lot of programmers' minds. So can there be a mechanism where programmers only need to define many tasks and let the system help us allocate these tasks to the CPU for concurrent execution?
The goroutine in the Go language is such a mechanism. The concept of a goroutine is similar to a thread, but the goroutine is scheduled and managed by the Go runtime. The Go program will intelligently allocate the tasks in the goroutine to each CPU. The Go language is called a modern programming language because it has built-in scheduling and context switching mechanisms at the language level.
In Go language programming, you don’t need to write processes, threads, and coroutines yourself. There is only one skill in your skill pack-goroutine. When you need to execute a task concurrently, you only need to package this task into one Function, just start a goroutine to execute this function, it's that simple and rude.

The use of Goroutine in Go

You gocan create a goroutine for a function by adding keywords in front of the function to be called

  • Example code:
import (
	"fmt"
)

func hello(){
    
    
	fmt.Println("Hello World!")
}
func main() {
    
    
	go hello()
	fmt.Println("hello main")
}

  • Running result:
    Insert picture description here
    here only the content in the main function is printed, but the content in the hello()function is not printed . The reason is that mainas the main function here , it is also a default Goroutine,mainfunction that ends when it returns, and of goroutinecourse the rest started in the main function can not be executed. You can time.sleepget the effect by adding one .
  • Code example:
import (
	"fmt"
	"time"
)

func hello(){
    
    
	fmt.Println("Hello World!")
}
func main() {
    
    
	go hello()
	fmt.Println("hello main")
	time.Sleep(time.Second)
}

  • Running effect:
    Insert picture description here
    Here, by time.sleepsuspending the execution of the mainfunction goroutine, mainthe return delay hello()can be executed.
  • The use of multiple goroutines The use of
    multiple goroutines can achieve the concurrency effect of the Go language.
import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func hello2(i int)  {
    
    
	defer wg.Done()//goroutine结束就登记-1
	fmt.Println("Hello goroutine",i)
}
func main() {
    
    
for i:=0;i<10;i++	{
    
    
	wg.Add(1)//启动一个goroutine就登记+1
	go hello2(i)
}
wg.Wait()//等到所有的goroutine结束
}

Insert picture description here

The main difference between goroutine and thread

  1. 线程: The basic unit of CPU scheduling and dispatch. There are often multiple threads in a process. Threads basically do not own resources but share the resources of the process, but each thread still has its auxiliary thread execution resources such as the program counter, a set of registers and the stack. The communication between threads mainly relies on shared memory, context switching is fast, and resource overhead is relatively small.
  2. 协程: The coroutine is a lightweight thread in user mode, and the scheduling of the coroutine is completely controlled by the user. The coroutine has its own registers, context switch and stack. When the coroutine is scheduled to switch, it saves the register context and stack in other places. When switching back, restores the previously saved register context and stack. Directly operating the stack basically has no core switching overhead and can be accessed without locking. Global variables, so context switching is very fast.
  • 区别
  1. A thread can have multiple coroutines, and a process can also have multiple coroutines separately, so that multi-core CPUs can be used in go.

  2. Thread processes are synchronous mechanisms, while coroutines are asynchronous

  3. The coroutine can retain the state of the last call, and each time the process is reentered, it is equivalent to entering the state of the last call

  4. OS threads (operating system threads) generally have a fixed stack memory (usually 2MB), a goroutine's stack has only a small stack at the beginning of its life cycle (typically 2KB), the goroutine's stack is not fixed, he It can be increased and decreased as needed, and the goroutine's stack size limit can reach 1GB, although this size is rarely used. Therefore, it is possible to create about 100,000 goroutines in the Go language at a time.

goroutine scheduling

GPM is the implementation of the run level of the Go language and a set of coroutine scheduling system implemented by the Go language. Different from the operating system scheduling OS threads.

  1. G represents a goroutine object, and every time go is called, a G is created
  2. P manages a set of goroutine queues. P will store the context (function pointer, stack address and address boundary) of the current goroutine. P will do some scheduling on the goroutine queue it manages (for example, the goroutine that takes a long time in the CPU) Pause, run subsequent goroutines, etc.) When your own queue is consumed, go to the global queue to fetch it. If the global queue is also consumed, it will go to other P queues to grab tasks.
  3. M is the virtual operation of go to the operating system kernel thread. M and the kernel thread generally have a one-to-one mapping relationship. A goroutine is ultimately executed on M.
    Insert picture description here
  1. P and M generally correspond one to one. Their relationship is: P manages a group of G mounts and runs on M. When a G is blocked on an M for a long time, the runtime will create a new M, and the P where the blocked G is located will mount other Gs on the newly created M. Reclaim the old M when the old G is blocked or it is considered dead.
  2. The number of P is set by runtime.GOMAXPROCS (maximum 256). After Go1.5 version, it defaults to the number of physical threads. When the amount of concurrency is large, some P and M will be added, but not too much. If the switching is too frequent, the gain will not be worth the loss.
  3. From the perspective of thread scheduling alone, the advantage of the Go language over other languages ​​is that OS threads are scheduled by the OS kernel, and goroutines are scheduled by the Go runtime's own scheduler. This scheduler uses a scheduler called m:n scheduling technology (reuse/scheduling m goroutines to n OS threads). One of its major features is that goroutine scheduling is done in user mode, and does not involve frequent switching between kernel mode and user mode, including memory allocation and release. It maintains a large memory pool in user mode. Calling the malloc function of the system directly (unless the memory pool needs to be changed) is much cheaper than scheduling OS threads. On the other hand, the multi-core hardware resources are fully utilized, and several goroutines are approximately equally divided among the physical threads. Coupled with the ultra-light weight of the goroutine itself, the above all ensure the performance of go scheduling.

Guess you like

Origin blog.csdn.net/H1517043456/article/details/112879368