Go Classic Primer Series 21: Go Coroutines

What are Go coroutines?

Goroutines are functions or methods that run concurrently with other functions or methods. Goroutines can be thought of as lightweight threads. Compared to threads, the cost of creating a Go routine is small. So in Go applications, it is common to see thousands of Go routines running concurrently.

Advantages of Go coroutines over threads

  • Compared to threads, Go coroutines are extremely cheap. The stack size is only a few kilobytes and can be increased or decreased according to the needs of the application. The thread must specify the size of the stack, the stack is fixed.

  • Go coroutines will multiplex (multiplex) fewer OS threads. Even if the program has thousands of Goroutines, there may be only one thread. If one of the Go routines in the thread is blocked (for example, waiting for user input), the system will create another OS thread and move the rest of the Go routines to this new OS thread. All of this happens at runtime, and as programmers, we don't face these intricate details directly, but instead have a neat API to handle concurrency.

  • Goroutines use channels to communicate. Channels are used to prevent race conditions when multiple coroutines access shared memory. Channels can be seen as pipes for communication between Goroutines. We'll discuss channels in detail in the next tutorial.

How to start a Go routine?

When calling a function or method, prepend the keyword  goto make a new Goroutine run concurrently.

Let's create a Go coroutine.

package main

import (
 "fmt"
)

func hello() {
 fmt.Println("Hello world goroutine")
}
func main() {
 go hello()
 fmt.Println("main function")
}

Run the program online[2]

On line 11, go hello() a new Go routine is started. Now  hello() functions and  main() functions will execute concurrently. The main function runs on a special Go routine called the Main Goroutine.

Run the program and you will be amazed!

The program will only output text  main function. What exactly went wrong with the Go routine we started? To understand all of this, we need to understand the main properties of two Go coroutines.

  • When a new coroutine is started, the invocation of the coroutine returns immediately. Unlike functions, program control does not wait for Goroutines to finish executing. Immediately after calling a Go coroutine, program control returns to the next line of code, ignoring any return value from that coroutine.

  • If you want to run other Goroutines, the main Goroutine must continue to run. If the main Goroutine terminates, the program terminates, so other Goroutines don't continue to run.

Now you should be able to understand why our Goroutine is not running. After the call on line 11  go hello() , program control does not wait for  the hello coroutine to end, and immediately returns to the next line of code, printing  main function. Then since there is no other executable code, the main Goroutine terminates, so  hello the coroutine has no chance to run.

We fix this now.

package main

import (
 "fmt"
 "time"
)

func hello() {
 fmt.Println("Hello world goroutine")
}
func main() {
 go hello()
 time.Sleep(1 * time.Second)
 fmt.Println("main function")
}

Run the program online[3]

On line 13 of the above program, we call the function `Sleep`[4] in the time package, which sleeps the Goroutine that executes it. Here, we put the Go main coroutine to sleep for 1 second. go hello() So the call has enough time to execute before the main coroutine terminates  . The program prints first  Hello world goroutine, waits 1 second, and then prints  main function.

Using sleep in the main Goroutine to wait for other coroutines to finish is just a trick to understand how Go coroutines work. Channels can be used to block the main Goroutine until other goroutines have finished executing. We'll discuss channels in the next tutorial.

Start multiple Go routines

To better understand Go routines, let's write another program that starts multiple Go routines.

package main

import (
 "fmt"
 "time"
)

func numbers() {
 for i := 1; i <= 5; i++ {
  time.Sleep(250 * time.Millisecond)
  fmt.Printf("%d ", i)
 }
}
func alphabets() {
 for i := 'a'; i <= 'e'; i++ {
  time.Sleep(400 * time.Millisecond)
  fmt.Printf("%c ", i)
 }
}
func main() {
 go numbers()
 go alphabets()
 time.Sleep(3000 * time.Millisecond)
 fmt.Println("main terminated")
}

Run the program online [5]

On lines 21 and 22 in the above program, two Goroutines are started. Now, the two coroutines run concurrently. numbers The coroutine first sleeps for 250 microseconds, then prints  1, then sleeps again, prints  2, and so on, until the  5end of printing. alphabete The coroutine also prints the letters from  a to  e and sleeps 400 microseconds each time. The Go main coroutine starts  numbers and  alphabete two Go coroutines, and terminates the program after sleeping for 3000 microseconds.

The program will output:

1 a 2 3 b 4 c 5 d e main terminated

The operation of the program is shown in the figure below. For a better view of the pictures, please open in a new tab.

picture

The first blue diagram represents the  numbers coroutine, the second maroon diagram represents the  alphabets coroutine, the third green diagram represents the Go main coroutine, and the last black diagram combines the above three coroutines, Indicate how the program is run. At the top of each box, strings such as  0 ms and  250 ms represent the time (in microseconds). At the bottom of each box, 1, 2, 3 etc. represent the output. The blue box means: 250 ms print out  1, 500 ms print out  2, and so on. The value at the bottom of the final black box will be  1 a 2 3 b 4 c 5 d e main terminated, which is also the output of the entire program. The above picture is very intuitive and you can use it to understand how the program works.

This concludes our introduction to Goroutines. wish you happiness.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324044122&siteId=291194637
Go