Go (2): package management, channel, coroutine concurrency, mutex basics

Generate package management filesgo-mod

The first step (initialize the creation of package management files)

insert image description here

$ go mod init go-app # go mod init 项目路径
/* go-app/go.mod */
module go-app

go 1.20

Step 2 (import package)

/* go-app/main.go */
package main

import "go-app/services"

func main() {
    
    
	services.Login("Lee", "123456")  // Lee 123456
	services.GetUserInfoById("1024") // 1024 Lee
}
/* go-app/services/login.go */
package services

import "fmt"

func Login(username string, password string) {
    
    
	fmt.Println(username, password)
}
/* go-app/services/user.go */
package services

import "fmt"

func GetUserInfoById(id string) {
    
    
	fmt.Println(id, "Lee")
}

Common commands

$ go run      # 编译并运行一个Go程序。
$ go build    # 编译一个Go程序,生成可执行文件。
$ go test     # 运行Go程序的测试用例。
$ go get      # 从远程仓库下载并安装Go包。
$ go install  # 编译并安装一个Go程序。
$ go fmt      # 格式化Go源码。
$ go doc      # 查看Go包的文档。
$ go mod      # 管理Go模块(依赖管理)。
$ go env      # 查看Go环境变量。
$ go version  # 查看Go版本信息

import remote package (example: gin)

The link address for querying the use of third-party packages is as follows:

https://pkg.go.dev/

The first step (import package)

import "github.com/gin-gonic/gin"

The second step (installation package)

$ go get -u github.com/gin-gonic/gin
# 解决无法安装问题

$ go env # 查看
# 修改如下
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct
module go-app

go 1.20

require (
	github.com/bytedance/sonic v1.8.7 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/gin-gonic/gin v1.9.0 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.12.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.3 // indirect
	github.com/mattn/go-isatty v0.0.18 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.0.7 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.8.0 // indirect
	golang.org/x/net v0.9.0 // indirect
	golang.org/x/sys v0.7.0 // indirect
	golang.org/x/text v0.9.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)

Step 3 (sample code)

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
    
    
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
    
    
		c.JSON(http.StatusOK, gin.H{
    
    
			"message": "pong",
		})
	})
	r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

insert image description here

process, thread, coroutine

process

In essence, it is an independently executed program. The process is the basic concept of resource allocation and scheduling by the operating system, and an independent unit for resource allocation and scheduling by the operating system.

thread

It is the smallest unit that the operating system can perform operation scheduling. It is included in the process and is the actual operating unit in the process. Multiple threads can run concurrently in one process, and each thread performs a different task, and the switching is controlled by the system.

coroutine

Also known as microthreads, it is a lightweight thread in user mode. Unlike threads and processes, coroutines need to perform context switching on the system kernel. The context switching of coroutines is determined by the user and has its own context. So a lightweight thread, also known as a user-level thread, is called a coroutine. A thread can have multiple coroutines. Thread processes are all synchronous mechanisms, while coroutines are asynchronous.

coroutinego ...

Adding a keyword in front of a function gomeans that a new sub-coroutine has been opened.

When the program starts, mainthe function execution will be regarded as the main coroutine, and all sub-coroutines will be automatically destroyed after the main coroutine is executed.

Coroutine example one

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

func request(method string, url string) {
    
    
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
    
    
		fmt.Println(err.Error())
		return
	}
	res, err := http.DefaultClient.Do(req)
	if err != nil {
    
    
		fmt.Println(err.Error())
		return
	}
	defer res.Body.Close()
	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
    
    
		fmt.Println(err.Error())
		return
	}
	fmt.Println(url, string(b))
}

请求同步进行,后请求等待最先请求出结果后出结果

func main() {
    
    
request("GET", "https://www.google.com/")
request("GET", "https://www.baidu.com/")
}

请求异步进行,先请求成功的先出结果

func main() {
    
    
go request("GET", "https://www.google.com/")
go request("GET", "https://www.baidu.com/")

time.Sleep(time.Millisecond * 60000)
}

Coroutine example two

非协程form

package main

import (
	"fmt"
	"net/http"
	"time"
)

var links = []string{
    
    
	"https://www.baidu.com/",
	"https://www.jd.com/",
	"https://www.taobao.com/",
	"https://www.abcd.com/",
	"https://www.sogou.com/",
	"https://www.csdn.net/",
}

func main() {
    
    
	// 0 https://www.baidu.com/ 	Success
	// 1 https://www.jd.com/ 		Success
	// 2 https://www.taobao.com/ 	Success
	// 3 https://www.abcd.com/ 		Fail
	// 4 https://www.sogou.com/ 	Success
	// 5 https://www.csdn.net/ 		Success
	for i, link := range links {
    
    
		_, err := http.Get(link)
		if err == nil {
    
    
			fmt.Println(i, link, "Success")
		} else {
    
    
			fmt.Println(i, link, "Fail")
		}
	}
}

协程form

package main

import (
	"fmt"
	"net/http"
	"time"
)

var links = []string{
    
    
	"https://www.baidu.com/",
	"https://www.jd.com/",
	"https://www.taobao.com/",
	"https://www.abcd.com/",
	"https://www.sogou.com/",
	"https://www.csdn.net/",
}

func main() {
    
    
	// 3 https://www.abcd.com/ 		Fail
	// 2 https://www.taobao.com/ 	Success
	// 1 https://www.jd.com/ 		Success
	// 0 https://www.baidu.com/ 	Success
	// 4 https://www.sogou.com/ 	Success
	// 5 https://www.csdn.net/ 		Success
	for i, link := range links {
    
    
		go func(i int, link string) {
    
    
			_, err := http.Get(link)
			if err == nil {
    
    
				fmt.Println(i, link, "Success")
			} else {
    
    
				fmt.Println(i, link, "Fail")
			}
		}(i, link)
	}

	time.Sleep(time.Millisecond * 10000)
}

aislechannel

A channel is a channel between coroutines that allows coroutines to communicate with each other.

passing messages through channels

The main thread of the following program will wait for the sub-thread to send a message. After receiving the first message, it will immediately execute the data receiving program in the main coroutine, and then the main thread will finish executing, so only one message will be printed out.

package main

import (
	"fmt"
	"net/http"
)

var links = []string{
    
    
	"https://www.baidu.com/",
	"https://www.jd.com/",
	"https://www.taobao.com/",
	"https://www.abcd.com/",
	"https://www.sogou.com/",
	"https://www.csdn.net/",
}

func main() {
    
    

	// 字符串类型通道
	msg := make(chan string)

	for i, link := range links {
    
    
		// 协程
		go func(i int, link string, msg chan string) {
    
    
			_, err := http.Get(link)
			if err == nil {
    
    
				// 发送消息
				msg <- fmt.Sprintln(i, link, "Success")
			} else {
    
    
				// 发送消息
				msg <- fmt.Sprintln(i, link, "Fail")
			}
		}(i, link, msg)
	}

	// 接收消息
	data := <-msg
	// 打印接收到的消息
	fmt.Println(data)
}

channel waiting

When the main coroutine fails to receive the message, the program will always be stuck in the process of receiving the message, so it can output the print results output by all sub-threads, or define multiple variables for receiving messages

package main

import (
	"fmt"
	"net/http"
)

var links = []string{
    
    
	"https://www.baidu.com/",
	"https://www.jd.com/",
	"https://www.taobao.com/",
	"https://www.abcd.com/",
	"https://www.sogou.com/",
	"https://www.csdn.net/",
}

func main() {
    
    

	// 字符串类型通道
	msg := make(chan string)

	for i, link := range links {
    
    
		// 协程
		go func(i int, link string, msg chan string) {
    
    
			_, err := http.Get(link)
			if err == nil {
    
    
				fmt.Println(i, link, "Success")
			} else {
    
    
				fmt.Println(i, link, "Fail")
			}
		}(i, link, msg)
	}

	// 接收消息
	data := <-msg
	// 打印接收到的消息
	fmt.Println(data)
}
package main

import (
	"fmt"
	"net/http"
)

var links = []string{
    
    
	"https://www.baidu.com/",
	"https://www.jd.com/",
	"https://www.taobao.com/",
	"https://www.abcd.com/",
	"https://www.sogou.com/",
	"https://www.csdn.net/",
}

func main() {
    
    

	// 字符串类型通道
	msg := make(chan string)

	for i, link := range links {
    
    
		// 协程
		go func(i int, link string, msg chan string) {
    
    
			_, err := http.Get(link)
			if err == nil {
    
    
				// 发送消息
				msg <- fmt.Sprintln(i, link, "Success")
			} else {
    
    
				// 发送消息
				msg <- fmt.Sprintln(i, link, "Fail")
			}
		}(i, link, msg)
	}
	// 打印接收到的消息
	fmt.Println(0, "--->", <-msg) // 0 ---> 3 https://www.abcd.com/ 	Fail
	fmt.Println(1, "--->", <-msg) // 1 ---> 1 https://www.jd.com/ 		Success
	fmt.Println(2, "--->", <-msg) // 2 ---> 2 https://www.taobao.com/ 	Success
	fmt.Println(3, "--->", <-msg) // 3 ---> 4 https://www.sogou.com/ 	Success
	fmt.Println(4, "--->", <-msg) // 4 ---> 0 https://www.baidu.com/ 	Success
	fmt.Println(5, "--->", <-msg) // 5 ---> 5 https://www.csdn.net/ 	Success
}

channel traversal

package main

import "fmt"

func main() {
    
    
	i := make(chan int)

	go func() {
    
    
		for j := 0; j < 5; j++ {
    
    
			i <- j
		}
		close(i) // 关闭通道
	}()

	//for {
    
    
	//	v, ok := <-i
	//	if ok {
    
    
	//		fmt.Println(v, ok)
	//	} else {
    
    
	//		break
	//	}
	//}

	for j := range i {
    
    
		fmt.Println(j)
	}

	//for j := 0; j < 10; j++ {
    
    
	//	fmt.Println(<-i)
	//}
}

WaitGroup

A program similar to the front-end axiosinterceptor to implement the loading state, and the loading state is closed only after all requests are completed

package main

import (
	"fmt"
	"net/http"
	"sync"
)

// 定义WaitGroup变量
var wg = sync.WaitGroup{
    
    }

var links = []string{
    
    
	"https://www.baidu.com/",
	"https://www.jd.com/",
	"https://www.taobao.com/",
	"https://www.abcd.com/",
	"https://www.sogou.com/",
	"https://www.csdn.net/",
}

func main() {
    
    
	for i, link := range links {
    
    
		// 协程
		go func(i int, link string) {
    
    
			// 每次完成请求后-1
			defer wg.Done()
			_, err := http.Get(link)
			if err == nil {
    
    
				fmt.Println(i, link, "Success")
			} else {
    
    
				fmt.Println(i, link, "Fail")
			}
		}(i, link)
		// 每次执行协程+1
		wg.Add(1)
	}

	// 等待
	wg.Wait()
}

mutexMutex

未加锁Coroutine

Asynchronous concurrent execution

insert image description here

package main

import (
	"fmt"
	"sync"
	"time"
)

var i = 0

var wg sync.WaitGroup

func add() {
    
    
	defer wg.Done()
	i++
	fmt.Printf("add->%v;", i)
	time.Sleep(time.Second * 2)
}

func sub() {
    
    
	defer wg.Done()
	i--
	fmt.Printf("sub->%v;", i)
}

/*
未加锁:(异步并发执行)

	结果1:add->1;add->1;sub->0;sub->0;add->1;sub->0;add->1;sub->0;add->2;sub->-1;-1
	结果2:add->1;sub->0;sub->-1;add->0;sub->-1;add->0;add->1;sub->1;add->2;sub->0;0
*/
func main() {
    
    
	for j := 0; j < 5; j++ {
    
    
		wg.Add(1)
		go add()
		wg.Add(1)
		go sub()
	}
	wg.Wait()
	fmt.Println(i)
}

Added 互斥锁coroutines

Under the concurrent program, use the mutual exclusion lock to realize synchronous sequential execution, wait for the unlock and then execute downward

insert image description here

package main

import (
	"fmt"
	"sync"
	"time"
)

var i = 0

var wg sync.WaitGroup

var mutex sync.Mutex

func add() {
    
    
	defer wg.Done()
	mutex.Lock()
	i++
	fmt.Printf("add->%v;", i)
	time.Sleep(time.Second * 2)
	mutex.Unlock()
}

func sub() {
    
    
	mutex.Lock()
	defer wg.Done()
	i--
	fmt.Printf("sub->%v;", i)
	mutex.Unlock()
}

/*
加锁后:(并发程序下利用互斥锁实现同步依次执行,等待解锁后在向下执行)

	结果1:add->1;sub->0;sub->-1;add->0;sub->-1;add->0;add->1;sub->0;sub->-1;add->0;0
	结果2:add->1;sub->0;add->1;sub->0;add->1;add->2;sub->1;sub->0;add->1;sub->0;0
	结果3:add->1;sub->0;sub->-1;add->0;sub->-1;add->0;sub->-1;add->0;sub->-1;add->0;0
*/
func main() {
    
    
	for j := 0; j < 5; j++ {
    
    
		wg.Add(1)
		go add()
		wg.Add(1)
		go sub()
	}
	wg.Wait()
	fmt.Println(i)
}

runtime

runtime.Gosched()

It is used to give up the execution permission of the current Goroutine, so that other Goroutines have the opportunity to run. The function of this function is to allow Goroutine to actively give up CPU time slices so that other Goroutines can run

Do not useruntime.Gosched()

package main

import (
	"fmt"
	"time"
)

func printNum(msg string, n int) {
    
    
	for i := 0; i < n; i++ {
    
    
		fmt.Print(msg, i, " ")
	}
}

// 结果
// a0 a1 a2 b0 b1 b2 b3 b4 Done
// b0 b1 b2 b3 b4 a0 a1 a2 Done
func main() {
    
    
	go printNum("a", 3)
	go printNum("b", 5)

	time.Sleep(1 * time.Second) // 主 Goroutine 等待 1 秒钟

	fmt.Println("Done")
}

useruntime.Gosched()

package main

import (
	"fmt"
	"runtime"
	"time"
)

func printNum(msg string, n int) {
    
    
	for i := 0; i < n; i++ {
    
    
		fmt.Print(msg, i, " ")
		runtime.Gosched() // 让出执行权限
	}
}

// 结果
// a0 b0 a1 a2 b1 b2 b3 b4 Done
// b0 b1 b2 b3 b4 a0 a1 a2 Done
// a0 b0 b1 a1 b2 a2 b3 b4 Done
// a0 b0 a1 b1 a2 b2 b3 b4 Done
func main() {
    
    
	go printNum("a", 3)
	go printNum("b", 5)

	time.Sleep(1 * time.Second) // 主 Goroutine 等待 1 秒钟

	fmt.Println("Done")
}

runtime.Gosched()purpose of use

Using the runtime.Gosched() function allows two Goroutines to compete for CPU resources more fairly

runtime.Goexit()

It is used to immediately terminate the execution of the current goroutine without affecting other goroutines or causing the entire program to exit. It can be understood as a mandatory return statement to return immediately from the current goroutine

package main

import (
	"fmt"
	"runtime"
	"time"
)

func printNumA() {
    
    
	for i := 0; i < 10; i++ {
    
    
		fmt.Print("A", i, " ")
		if i >= 5 {
    
    
			runtime.Goexit()
		}
	}
}

func printNumB() {
    
    
	for i := 0; i < 10; i++ {
    
    
		fmt.Print("B", i, " ")
	}
}

// 结果
// A0 A1 A2 A3 A4 A5 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 Done
// B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 A0 A1 A2 A3 A4 A5 Done
func main() {
    
    
	go printNumA()
	go printNumB()

	time.Sleep(1 * time.Second) // 主 Goroutine 等待 1 秒钟

	fmt.Println("Done")
}

runtime.GOMAXPROCS(n) runtime.NumCPU()

runtime.GOMAXPROCS(n)Set the number of CPU cores used by the program, the default is the most cores

runtime.NumCPU()Get the current number of CPU cores

default

package main

import (
	"fmt"
	"runtime"
	"time"
)

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

func b() {
    
    
	for i := 0; i < 20; i++ {
    
    
		fmt.Print("b")
	}
}

func main() {
    
    
	fmt.Print(runtime.NumCPU()) // 12 获取当前CPU核心数

	// 默认结果(交替出现)
	// 12 bbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaabaaaab
	go a()
	go b()

	time.Sleep(time.Second)
}

single core

package main

import (
	"fmt"
	"runtime"
	"time"
)

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

func b() {
    
    
	for i := 0; i < 20; i++ {
    
    
		fmt.Print("b")
	}
}

func main() {
    
    
	fmt.Print(runtime.NumCPU()) // 12 获取当前CPU核心数

	// 单核结果(顺序出现)
	//runtime.GOMAXPROCS(1)  12 bbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa
	runtime.GOMAXPROCS(1) // 设置为单核
	go a()
	go b()

	time.Sleep(time.Second)
}

Dual-core

package main

import (
	"fmt"
	"runtime"
	"time"
)

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

func b() {
    
    
	for i := 0; i < 20; i++ {
    
    
		fmt.Print("b")
	}
}

func main() {
    
    
	fmt.Print(runtime.NumCPU()) // 12 获取当前CPU核心数

	// 双核结果(交替出现)
	// runtime.GOMAXPROCS(2)  12 bbbaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbaaaaa
	// runtime.GOMAXPROCS(2)  12 aaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbb
	// runtime.GOMAXPROCS(2)  12 aaaaaaabbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaa
	runtime.GOMAXPROCS(2) // 设置为双核
	go a()
	go b()

	time.Sleep(time.Second)
}

select

It is used to monitor the input and output of multiple channels, which can realize non-blocking communication and wait for the function of multiple channel operations

In select, each case must be a channel operation. When the select function is called, it waits for one of the case operations to complete, and then executes the case statement block. If multiple case operations are completed at the same time, Go will randomly select one of them

package main

import (
	"fmt"
	"time"
)

func main() {
    
    
	c1 := make(chan string)
	c2 := make(chan string)

	go func() {
    
    
		c1 <- "Hello"
	}()

	go func() {
    
    
		c2 <- "World"
	}()

	for {
    
    
		select {
    
    
		case msg1 := <-c1:
			fmt.Println(msg1)
		case msg2 := <-c2:
			fmt.Println(msg2)
		default: // 避免死锁
			fmt.Println("default")
		}
		time.Sleep(time.Second)
	}
}
package main

import (
	"fmt"
	"time"
)

func main() {
    
    
	c1 := make(chan string)
	c2 := make(chan string)

	go func() {
    
    
		c1 <- "Hello"
		close(c1) // 避免死锁
	}()

	go func() {
    
    
		c2 <- "World"
		close(c2) // 避免死锁
	}()

	for {
    
    
		select {
    
    
		case msg1 := <-c1:
			fmt.Println(msg1)
		case msg2 := <-c2:
			fmt.Println(msg2)
		}
		time.Sleep(time.Second)
	}
}

time.NewTimer

Used to create a new timer and return the timer, triggering an event after the specified time

package main

import (
	"fmt"
	"time"
)

func main() {
    
    
	timer := time.NewTimer(2 * time.Second)

	fmt.Println("计时器已启动")

	<-timer.C // 阻塞等待计时器的时间到达

	fmt.Println("时间到!")
}

stop timer

package main

import (
	"fmt"
	"time"
)

/**
 * 结果:
 *      计时器已启动
 *      时间到!
 */
func main() {
    
    
	timer := time.NewTimer(5 * time.Second)

	fmt.Println("计时器已启动")

	time.Sleep(1 * time.Second)
	if !timer.Stop() {
    
    
		fmt.Println(<-timer.C)
	}

	fmt.Println("时间到!")
}
package main

import (
	"fmt"
	"time"
)

/**
 * 结果:
 *      计时器已启动
 *      2023-05-02 12:06:31.980288 +0800 CST m=+5.001244626
 *      时间到!
 */
func main() {
    
    
	timer := time.NewTimer(5 * time.Second)

	fmt.Println("计时器已启动")

	time.Sleep(6 * time.Second)
	if !timer.Stop() {
    
    
		fmt.Println(<-timer.C) // 2023-05-02 12:06:31.980288 +0800 CST m=+5.001244626
	}

	fmt.Println("时间到!")
}

reset timer

package main

import (
	"fmt"
	"time"
)

/**
 * 结果:
 * 		计时器已启动
 * 		2023-05-02 12:11:00.035369 +0800 CST m=+1.001328668
 * 		时间到!
 */
func main() {
    
    
	timer := time.NewTimer(5 * time.Second)

	fmt.Println("计时器已启动")
	timer.Reset(time.Second) // 重置定时器为1s

	fmt.Println(<-timer.C)

	fmt.Println("时间到!")
}

time.NewTicker

Example of timer and stop timer

package main

import (
	"fmt"
	"time"
)

func main() {
    
    
	// 创建一个定时器,每隔2秒钟执行一次
	ticker := time.NewTicker(time.Second * 2)

	count := 0

	for t := range ticker.C {
    
    
		count++
		fmt.Println(t)
		if count == 3 {
    
    
			ticker.Stop()
			fmt.Println("定时器停止!")
			break
		}
	}
}

Concurrent Atomic Operationssync/atomic

Refers to one or a series of operations that will not be interrupted, and the atomicity of the operation can be guaranteed even under concurrent conditions

Coroutine accumulating and decrementing variables

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

var wg sync.WaitGroup

var i int32 = 100

func add() {
    
    
	defer wg.Done()
	atomic.AddInt32(&i, 1)
}

func sub() {
    
    
	defer wg.Done()
	atomic.AddInt32(&i, -1)
}

func main() {
    
    
	for {
    
    
		for j := 0; j < 100; i++ {
    
    
			wg.Add(1)
			go add()
			wg.Add(1)
			go sub()
		}
		wg.Wait()
		fmt.Println(i) // 100 100 100 100 100 100 ...
	}
}

Guess you like

Origin blog.csdn.net/weixin_43526371/article/details/130467921