生成包管理文件go-mod

第一步(初始化创建包管理文件)

Go(二):包管理、通道、协程并发、互斥锁基础-LMLPHP

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

go 1.20

第二步(导入包)

/* 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")
}

常用命令

$ 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版本信息

导入远程包(示例:gin

第一步(导入包)

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

第二步(安装包)

$ 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
)

第三步(示例代码)

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")
}

Go(二):包管理、通道、协程并发、互斥锁基础-LMLPHP

进程、线程、协程

进程

线程

协程

协程 go ...

协程示例一

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)
}

协程示例二

非协程形式

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")
		}
	}
}

协程形式

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)
}

通道channel

通过通道传递消息

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)
}

通道等待

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
}

通道遍历

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

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()
}

互斥锁Mutex

未加锁协程程序

Go(二):包管理、通道、协程并发、互斥锁基础-LMLPHP

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)
}

加了互斥锁的协程程序

Go(二):包管理、通道、协程并发、互斥锁基础-LMLPHP

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()

不使用runtime.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")
}

使用runtime.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()目的

runtime.Goexit()

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()

默认

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)
}

单核

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)
}

双核

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

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

package main

import (
	"fmt"
	"time"
)

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

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

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

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

停止定时器

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("时间到!")
}

重置定时器

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

定时器和停止定时器的示例

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
		}
	}
}

并发之原子操作sync/atomic

协程累加累减变量

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 ...
	}
}
05-03 04:42