Package management, coroutine concurrency basis
Generate package management filesgo-mod
The first step (initialize the creation of package management files)
$ 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:
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")
}
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
go
means that a new sub-coroutine has been opened.When the program starts,
main
the 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
axios
interceptor 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
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
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 ...
}
}