Golang learning road 6-goroutine concurrency


Preface

What is a goroutine? The abbreviation can be: go process, concurrency

  • A goroutine is a function or method that runs concurrently with other functions or methods.
  • Goroutine can be considered as a lightweight thread, which naturally supports multiple concurrency.
  • The cost of creating goroutines is small compared to threads, so Go applications often have thousands of goroutines running concurrently.

1. Usage of goroutine

Go language inherently supports multiple concurrency, how to use it:

  • Method 1: go func()
  • Method 2 (anonymous): go func(){}()
package main
import (
	"fmt"
	"time"
)
func display(count int) {
    
    
	for {
    
    
		fmt.Println("这是子go程======>:", count)
		time.Sleep(1)
		count++
	}
}

func main() {
    
    
	go display(1)

	// 匿名 子go程 函数
	//go func () {
    
    
	//	count := 1
	//	for {
    
    
	//		fmt.Println("这是子go程======>:", count)
	//		time.Sleep(1)
	//		count++
	//	}
	//}()
	
	// 主go程
	count := 1
	for {
    
    
		fmt.Println("这是主go程:", count)
		count++
		time.Sleep(1)
		if count > 2 {
    
    
			break
		}
	}
}

It can be seen that the main process did not print the third time, but the child process printed the third time
Insert image description here

2. Goroutine loop

For example, if three processes are started to run goroutine, the cpu resources will be competed and it is not sure who will come first:

for i := 0; i <= 3; i++ {
    
    
	go func() {
    
    }()
}

3. Goroutine exits early

Three ways to exit the go program:

  • return: Only exit the current sub-go process function
  • runtime.Goexit(): Only exit the current go process
  • os.Exit(-1): Exit all master and sub-go processes
package main

import (
	"fmt"
	"os"
	"time"
)

func main() {
    
    
	go func() {
    
    
		func() {
    
    
			fmt.Println("这是子go程 内部函数111")
			//return 			  // 只结束此函数,会执行到:子go程222
			//runtime.Goexit()    // 退出当前go程,不会执行到:子go程222
			os.Exit(-1)	          // 退出所以主子进程
		}()
		fmt.Println("子go程222")
	}()
	time.Sleep(1)
	fmt.Println("结束主go程!")
}

4. Goroutine bidirectional pipeline

Channels can be used to synchronize execution and communication between two goroutines by passing a value of a specified type. The operator <- is used to specify the direction of the channel, send or receive. If no direction is specified, the channel is bidirectional.

ch <- value    // 把 value  发送到通道 ch
value := <-ch  // 从 ch 接收数据,并把值赋给 value 

chan bidirectional pipe:

  • No need to lock or unlock when using communication;
  • When the capacity is not created, it is unbuffered, otherwise it is buffered;
  • When the buffer is full or when the read is complete, it will block, and resume writing after being read;
  • The number of reads and writes in the pipe must be equal, otherwise it will be blocked. ① If the main go process is blocked, the program will be locked until it crashes. ② If it is blocked in the sub-go process, a memory leak will occur;
  • You need to use make to allocate space, otherwise the pipeline defaults to nil, and reading and writing will be blocked (same as map, so it is recommended to use make);
  • It is recommended to use for range when using goroutine pipelines, and close(chan) should be closed when the pipeline is written
package main
import (
	"fmt"
	"time"
)

func main() {
    
    
	// 创建一个容量为10的int有缓冲管道
	numChan := make(chan int, 10)

	// 写“管道”数据
	go func() {
    
    
		for i := 1; i < 21; i++ {
    
    
			numChan <- i
			fmt.Println("11-->子go程写入数据:", i)
		}
		// 写入结束,关闭管道
		close(numChan)
	}()

	// 读“管道”数据
	go func() {
    
    
		for num := range numChan {
    
    
			fmt.Println("22-->子go程读取数据data:", num)
		}
	}()

	time.Sleep(1 * time.Second)

	// 判断管道是否关闭
	for {
    
    
		v, ok := <-numChan
		if !ok {
    
    
			fmt.Println("管道已经关闭,准备退出!!")
			break
		}
		fmt.Println("v:", v)
	}
}

5. Goroutine one-way pipeline

Generally used for function parameters, such as read-only and write-only one-way pipes, see what is the difference between the following and two-way pipes?
Producer: chan<
-consumer: <-chan
when bidirectional pipeline is: <-numChan

package main
import (
	"fmt"
	"time"
)

// 生产者:只写入数据
func producer(out chan<- int) {
    
    
	for i := 1; i < 11; i++ {
    
    
		out <- i
		fmt.Println("写入数据:", i)
	}
	close(out)
}

// 消费者:只读数据
func consumer(in <-chan int) {
    
    
	for v := range in {
    
    
		fmt.Println("读取数据:", v)
	}
}

func main() {
    
    
	// 创建一个双向通道
	numChan := make(chan int, 5)

	// 创建生产者
	go producer(numChan)

	// 创建消费者
	go consumer(numChan)

	time.Sleep(200)
}

Insert image description here

6. Monitoring pipeline

When multi-go threads are concurrent, we can monitor the corresponding data through select. Suddenly, it feels like a message event. One is responsible for pushing and the other is responsible for monitoring... select:
monitor multiple pipes (write/read and write data, close pipes)

package main
import (
	"fmt"
	"time"
)

func main() {
    
    
	// 创建两个管道,用来测试监听
	chan1 := make(chan int)
	chan2 := make(chan int)
	// 监听go程
	go func() {
    
    
		for {
    
    
			select {
    
    
			case data1 := <-chan1:
				fmt.Println("监听到chan111:", data1)
			case data2 := <-chan2:
				fmt.Println("监听到chan222:", data2)
			default:
				fmt.Println("正常监听中...")
				time.Sleep(2 * time.Second)
			}
		}
	}()

	// go程:两个管道分别写入数据
	go func() {
    
    
		for i := 0; i < 5; i++ {
    
    
			chan1 <- i
			i++
			chan2 <- i
		}
	}()

	count := 1
	for {
    
    
		time.Sleep(10 * time.Second)
		if count == 6 {
    
    
			break
		}
		count++
	}
}

As shown in the figure below, we can see that when we monitor that there is written data, we will get the corresponding type of data. When there is no written data, default has been responsible for monitoring!
Insert image description here

Summarize

The above is what we have learned today. This article only briefly introduces the use of goroutine. Please read more about how to practice it in the project!

Guess you like

Origin blog.csdn.net/qq_42675140/article/details/127819216