I’m participating in nuggets Creators Camp # 4, click here to learn more and learn together!

goroutine

Goroutine is one of the most basic organizational units in Go and is one of the most important features of Go’s support for native concurrency.

In fact, every Go program has at least one: main Gotoutine, which is automatically created and started when the program starts.

Simply put, a gotoutine is a concurrent function (remember: not necessarily in parallel) that runs alongside other code.

The difference between concurrency and parallelism: The parallel mechanism refers to the simultaneous execution of multiple entities of a type, whereas concurrency is a way of building components and being able to execute them independently if necessary.

You can simply start a function by placing the go keyword in front of it:

func main(a) {
	go sayHello()
}

func sayHello(a) {
	fmt.Println("hello")}Copy the code

The same can be done for anonymous functions, as you can see clearly from the following example. In the following example, instead of creating a goroutine from a function, we create a Goroutine from an anonymous function:

go func(a) {
fmt.Println("hello")
}()/ / 1
// continue doing other things
// Notice () here, we must call the anonymous function immediately to make the go keyword valid. Alternatively, you can assign the function to a variable and call it like this:

sayHello := func(a) {
	fmt.Println("hello")}go sayHello()
// continue doing other things
Copy the code

Seems pretty simple, right? We can create a block of concurrent logic with a function and a keyword, and that’s all we need to know to start Goroutine. Of course, there’s a lot more to be said about how to use it properly, how to synchronize it, and how to organize it.

Goroutine and how does it work

This section answers the following three questions: How does Goroutine actually work? Is goroutine an operating system thread? Or is goroutine a green thread? How many Goroutines can we create?

Goroutines are unique to Go (although some other languages have similar concurrency primitives). They are not operating system threads, they are not exactly green threads (threads managed by the language runtime), they are higher-level abstractions called coroutines. Coroutines are non-preemptive and emit subroutines, that is, they cannot be interrupted.

What makes Go unique is goutine’s deep runtime integration with Go. Goroutine does not define its own pause or reentry point; The Go language watches goroutine behavior at run time, automatically suspsuspes them when they block, and then resumes them when they become unblocked. To some extent, this allows them to preempt, but only where the Goroutine is blocked. It is an elegant partnership between the runtime and Goroutine logic. Therefore, goroutine can be considered a special kind of coroutine.

What is a coroutine

Coroutines can be thought of as lightweight threads. Unlike threads, the operating system kernel is unaware of the existence of coroutines.

The management of coroutines relies on the scheduler provided by the Go language runtime itself. Can also be thought of as an implicit concurrency construct of Goroutine, but concurrency is not a property inherent to coroutines: the scheduler must be able to host several coroutines at once and give each a chance to execute, or they cannot achieve concurrency. Of course, it is possible to have several coroutines executed sequentially, but it looks like parallelism.

Coroutines are user-mode, and the Go language coroutines are subordinate to a thread. The Go scheduler implements what is called an M: N scheduler through multiplexing, meaning that it maps M coroutines to N system threads. The Go scheduler is represented as the Go component that is responsible for the execution and sequence of the coroutines in the Go program. Everything in the Go program will be executed as a coroutine.

Create a coroutine

You can create a Goroutine with regular and anonymous functions, such as a new main.go file, and write the following code:

package main

import (
	"fmt"
	"time"
)

func printNum(a) {
	for i := 0; i < 10; i++ {
		fmt.Print(i)
	}
}

func main(a) {
	go printNum()	// Regular functions create coroutines

	// Anonymous function will run the coroutine
	go func(a) {
		for i := 10; i < 20; i++ {
			fmt.Print(i, "")
		}
	}()

	time.Sleep(1 * time.Second)
	fmt.Println()
}

Copy the code

If you run this code, you may not get the same result each time:

[Running] go run "/Users/yuzhou_1su/GoProjects/ go Concurrency /v1/main.go" 010 11 12 13 14 15 16 17 18 19 123456789 [Running] go Run "/Users/yuzhou_1su/GoProjects/Go Concurrency /v1/main. Go "01210 11 12 13 14 15 16 17 18 19 3456789Copy the code

The different results of each run indicate that without additional operations on coroutines, there is no control over the order in which coroutines are executed.

Create multiple coroutines

Read the command line dynamic number of coroutines through flag, such as flag.int to read the command line option value to determine the number of coroutines to be created. If no value is passed in, the value of n will become 10.

Using the for loop to generate the required number of coroutines, the coroutine runs fast, only giving the thread enough time to finish its task with the time.sleep() statement so that we can print the final result at the command line. If you do not use this statement, you may not see the complete result, so try it out for yourself.

package main

import (
	"flag"
	"fmt"
	"time"
)

func main(a) {
	n := flag.Int("num".10."Number of goroutines")
	flag.Parse()

	count := *n
	fmt.Printf("Going to create %d goroutine.\n", count)

	for i := 0; i < count; i++ {
		go func(x int) {
			fmt.Printf("%d ", x)
		}(i)
	}

	time.Sleep(time.Second)
	fmt.Println("\nExiting...")}Copy the code

Run the code:

$ go run main.go -num 100
Going to create 100 goroutine.
5 3 4 0 29 17 18 19 16 6 7 8 9 10 11 12 13 14 15 20 1 22 21 44 55 30 32 33 34 35 36 37 38 39 2 23 41 27 24 52 26 47 31 42 25 28 43 40 70 54 45 46 66 67 56 60 68 62 59 72 76 78 58 82 83 50 80 91 96 61 90 98 77 87 49 73 74 53 79 75 65 48 84 63 94 71 51 92 64 93 89 85 97 69 88 86 57 99 95 81 
Exiting...
$ go run main.go -num 100
Going to create 100 goroutine.
13 7 8 9 10 11 12 0 1 36 14 15 16 17 18 19 20 3 6 4 5 28 22 23 24 21 27 30 31 33 29 2 34 48 25 57 35 37 49 50 51 52 53 54 55 56 42 39 38 32 44 40 74 89 58 75 87 47 60 94 26 64 41 92 85 67 81 46 43 88 83 77 80 90 63 91 84 96 59 65 99 66 86 93 79 76 61 98 68 69 82 45 78 72 70 71 62 73 95 97 
Exiting...
Copy the code

conclusion

Goroutine is a lightweight threading implementation of the Go language, known as a coroutine. It is managed at run time by the Go scheduler. There are two ways to create a Goroutine, either by preempting a regular function directly with the keyword Go, or by declaring an anonymous function.

The goroutine declared by GO at runtime is executed concurrently in the new goroutine, and the goroutine is terminated automatically when the called function returns.

It is worth noting that multiple coroutines generally do not have a sequential order, only through certain means (such as channels, locks, etc.) to determine the order.

During the learning process, we will use time.Sleep to wait for the output of the coroutine to run, without actually needing a time delay.

Finally, learning goroutine is just the first step in learning Go concurrency, and there’s more to explore. See you in the next article!

References:

  • Mastering the Go Language (2nd edition)
  • Anatomy of the Basic Principles of Go language