文章目录
- 一、本文重点内容:
- 二、详细知识点介绍:
- 1、并发和并行
- 并发:
- 并行:
- 结论:
- 2、Go的协程
- 协程:
- 线程:
- 3、协程通信
- 方式一:使用通道交换数据
- 方式二:使用共享内存完成数据交换
- 4、协程通道
- 5、WaitGroup计数器
- 6、Go的依赖管理演进
- GOPATH
- Go Vendor
- Go Modoule
- 7、Go依赖配置(go.mod)
- 版本:
- 8、工具 go.get/go.mod
- 三、实践练习例子:
- 1、协程使用:快速打印协程编号
- 2、协程通道使用
- 3、并发安全 Lock
- 4、语法熟悉——猜谜小游戏
- 5、语法熟悉——彩云翻译数据爬取
- 四、课后个人总结:
一、本文重点内容:
-
本堂课的知识要点有哪些?
go语言进阶
1、并发和并行
2、Go的协程
3、协程通信
4、协程通道
5、WaitGroup计数器
go的依赖管理
1、Go的依赖演进
2、Go依赖配置
3、工具(go.get/go.mod)
二、详细知识点介绍:
1、并发和并行
并发:
并行:
结论:
go可以充分发挥多核并发的优势,高效运行。
可以说go就是为了并发而生的。
2、Go的协程
协程:
用户态,轻量级线程,栈大小:KB级别。
go语言一次可以创建上万协程。
线程:
内核态,线程上可以运行多个协程,比较消耗资源。
栈大小:MB级别。
3、协程通信
方式一:使用通道交换数据
方式二:使用共享内存完成数据交换
提倡使用通道实现协程通信
4、协程通道
创建方式:make(chan,元素类型,[缓冲大小])
分类:
1、无缓冲通道:同步通道,保证两个协程同步。
2、有缓冲通道:典型生产消费模型。
5、WaitGroup计数器
示例代码:
//go:build ignore
// +build ignore
package main
import "sync"
func main() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
println(j)
}(i)
}
wg.Wait()
}
运行结果:
6、Go的依赖管理演进
GOPATH
1、项目代码直接依赖src下的代码
2、go get下载最新版本的包到src目录下
弊端:
无法实现pkg的多版本控制
Go Vendor
项目目录下增加vendor文件,所有依赖包副本形式放在$ProjectRoot/vendor
依赖寻址方式: vendor => GOPATH
通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题。
弊端:
问题
无法控制依赖的版本。
更新项目又可能出现依赖冲突,导致编译出错。
Go Modoule
通过go.mod文件管理依赖包版本
通过go get/go mod指令工具管理依赖包
三要素:
1.配置文件,描述依赖:go.mod
2.中心仓库管理依赖库:Proxy
3.本地工具:go get/mod
7、Go依赖配置(go.mod)
版本:
注:
1、主版本2+模块会在模块路径增加/vN后缀。
2、对于没有go.mod文件并且主版本2+的依赖,会+incompatible
8、工具 go.get/go.mod
三、实践练习例子:
1、协程使用:快速打印协程编号
代码:
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 5; i++ {
go func(j int) {
holle(j)
}(i)
}
time.Sleep(time.Second)
}
func holle(i int) {
println("holle goroutine:" + fmt.Sprint(i))
}
运行结果:
可以看到确实是多个协程共同运行
2、协程通道使用
目的:
A子协程发送0~9数字
B子协程计算输入数字的平方
主协程输出最后的平方数
代码:
//go:build ignore
// +build ignore
package main
import "fmt"
func main() {
// 创建两个通道
src := make(chan int)
dest := make(chan int, 2)
// 协程A
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
// 协程B
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
// 主协程
for i := range dest {
fmt.Println(i)
}
}
运行结果:
3、并发安全 Lock
目的:对变量执行2000次+1操作,5个协程并发执行
代码:
package main
import (
"sync"
"time"
)
func main() {
add()
}
var (
x int64
lock sync.Mutex
)
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock()
x++
lock.Unlock()
}
}
func addWithoutLock() {
for i := 0; i < 2000; i++ {
x++
}
}
func add() {
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
println("使用锁:", x)
x = 0
for i := 0; i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
println("不使用锁:", x)
}
运行结果:
可以看到使用了锁后,保证了共享变量的数据可见性。
4、语法熟悉——猜谜小游戏
目的:玩家随机猜测一个1~100的数,返回是否正确,不正确返回与目标数的大小关系。
代码:
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
// 生成随机数
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNum := rand.Intn(maxNum)
// fmt.Println("最终答案:", secretNum)
fmt.Println("请输入1~100中随机一个数:")
count := 0
for {
// 输入猜测数
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("输入错误,请重试!!!")
count++
continue
}
input = strings.TrimSuffix(input, "\r\n")
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("不是整数,请重试!!!", err)
count++
continue
}
fmt.Println("你的猜测数:", guess)
if guess < 1 && guess > 100 {
fmt.Println("数字不在范围内,重新输入:")
count++
continue
}
// 判断是否正确
count++
if guess < secretNum {
fmt.Println("数字小于答案,请再次尝试")
} else if guess > secretNum {
fmt.Println("数字大于答案,请再次尝试")
} else {
fmt.Println("恭喜你答对了!!!")
fmt.Println("一共花费了:", count, "次")
break
}
}
}
测试:
GOROOT=D:\Goland\go1.19.2 #gosetup
GOPATH=C:\Users\lenovo\go #gosetup
D:\Goland\go1.19.2\bin\go.exe build -o C:\Users\lenovo\AppData\Local\Temp\GoLand\___go_build_TestDemo1_go.exe C:\Users\lenovo\go\src\awesomeProject\main\TestDemo1.go #gosetup
C:\Users\lenovo\AppData\Local\Temp\GoLand\___go_build_TestDemo1_go.exe
请输入1~100中随机一个数:
50
你的猜测数: 50
数字大于答案,请再次尝试
25
你的猜测数: 25
数字小于答案,请再次尝试
38
你的猜测数: 38
数字小于答案,请再次尝试
44
你的猜测数: 44
数字大于答案,请再次尝试
41
你的猜测数: 41
数字小于答案,请再次尝试
42
你的猜测数: 42
数字小于答案,请再次尝试
43
你的猜测数: 43
恭喜你答对了!!!
一共花费了: 7 次
进程 已完成,退出代码为 0
5、语法熟悉——彩云翻译数据爬取
目的:输入一个单词,爬取翻译的相关信息
代码:
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
)
type DictRequest struct {
trans_type string
source string
}
func main() {
client := &http.Client{}
// 注意,"和`的混合使用
var data = strings.NewReader(`{"trans_type": "en2zh","source": "good"}`)
request, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err != nil {
log.Fatal(err)
}
request.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.35")
request.Header.Set("content-type", "application/json;charset=UTF-8")
request.Header.Set("origin", "https://fanyi.caiyunapp.com")
request.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
do, err := client.Do(request)
if err != nil {
log.Fatal(err)
}
defer do.Body.Close()
text, err := ioutil.ReadAll(do.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", text)
}
四、课后个人总结:
这节课熟悉了语法知识,写了很多go的代码。也了解了Go在并发并行方面的知识,Go的依赖管理方面的配置也明白了不少。