Go资料
安装go
1. 先执行
uname -m
查看系统架构
root@i-k5d6x5d6:~/cli# uname -m
x86_64
2. 获取go 语言包
wget https://go.dev/dl/go1.18.linux-amd64.tar.gz
3. 解压
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.5.linux-amd64.tar.gz
4. 导入环境变量
export PATH=$PATH:/usr/local/go/bin
5. 验证,查看go版本
go version
1. 依赖更新
go mod init # 初始化go.mod
go mod tidy # 更新依赖文件
go mod download # 下载依赖文件
go mod vendor # 将依赖转移至本地的vendor文件
go mod edit # 手动修改依赖文件
go mod graph # 打印依赖图
go mod verify # 校验依赖
2. 单独安装依赖
go get github.com/gin-gonic/gin
如何删除GO语言中安装的包
直接去手动删除:
C:\Users\Administrator\go\ 目录下对应的包,有两个文件夹 分别是 src 和 pkg 一个是源文件 一个是包,想要删掉哪个包,就删除src 和 pkg 中对应的文件 src 就是源文件,pkg 中是一个.a的文件
Go语言中的goroutine就是这样一种机制,goroutine的概念类似于线程,但 goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU。Go语言之所以被称为现代化的编程语言,就是因为它在语言层面已经内置了调度和上下文切换的机制。
3. 协程和线程
协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。
线程:一个线程上可以跑多个协程,协程是轻量级的线程。
goroutine 只是由官方实现的超级"线程池"。
每个实例 4~5KB 的栈内存占用和由于实现机制而大幅减少的创建和销毁开销是go高并发的根本原因。
goroutine 奉行通过通信来共享内存,而不是共享内存来通信
goroutine与线程
可增长的栈,OS线程(操作系统线程)一般都有固定的栈内存(通常为2MB),一个goroutine的栈在其生命周期开始时只有很小的栈(典型情况下2KB),goroutine的栈不是固定的,他可以按需增大和缩小,goroutine的栈大小限制可以达到1GB,虽然极少会用到这个大。所以在Go语言中一次创建十万左右的goroutine也是可以的。
goroutine调度
GPM是Go语言运行时(runtime)层面的实现,是go语言自己实现的一套调度系统。区别于操作系统调度OS线程。
- 1.G很好理解,就是个goroutine的,里面除了存放本goroutine信息外 还有与所在P的绑定等信息。
- 2.P管理着一组goroutine队列,P里面会存储当前goroutine运行的上下文环境(函数指针,堆栈地址及地址边界),P会对自己管理的goroutine队列做一些调度(比如把占用CPU时间较长的goroutine暂停、运行后续的goroutine等等)当自己的队列消费完了就去全局队列里取,如果全局队列里也消费完了会去其他P的队列里抢任务。
- 3.M(machine)是Go运行时(runtime)对操作系统内核线程的虚拟, M与内核线程一般是一一映射的关系, 一个groutine最终是要放到M上执行的;
P与M一般也是一一对应的。他们关系是: P管理着一组G挂载在M上运行。当一个G长久阻塞在一个M上时,runtime会新建一个M,阻塞G所在的P会把其他的G 挂载在新建的M上。当旧的G阻塞完成或者认为其已经死掉时 回收旧的M。
类似于线程中的join
多次执行上面的代码,会发现每次打印的数字的顺序都不一致。这是因为10个goroutine是并发执行的,而goroutine的调度是随机的。
var wg sync.WaitGroup func hello(i int) { defer wg.Done() // goroutine结束就登记-1 fmt.Println("Hello Goroutine!", i) } func main() { for i := 0; i < 10; i++ { wg.Add(1) // 启动一个goroutine就登记+1 go hello(i) } wg.Wait() // 等待所有登记的goroutine都结束 }
Channel
单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。
虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题。为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法势必造成性能问题。
Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。
如果说goroutine是Go程序并发的执行体,channel就是它们之间的连接。channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制。
Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型。

channel分为两种,1.有缓冲管道 2.无缓冲管道
1. 无缓冲的通道只有在有人接收值的时候才能发送值
通道总结

4. *和&的区别 :
- & 是取地址符号 , 即取得某个变量的地址 , 如 ; &a
- *是指针运算符 , 可以表示一个变量是指针类型 , 也可以表示一个指针变量所指向的存储单元 , 也就是这个地址所存储的值 .

5. 字符串映射函数操作
package main import ( "fmt" "reflect" ) type IRoute interface{ test() } type Common struct{} func (c *Common) test() { fmt.Println("test") } type Login struct{ Common } func (l *Login) test() { fmt.Println("Login test ---------1") } type Auth struct{ Common } func (a *Auth) test() { fmt.Println("Auth test ------------2") } func addroute(route IRoute) { route.test() } var RegisterMessage = make(map[string]interface{}) func init() { RegisterMessage["login"] = &Login{} RegisterMessage["auth"] = &Auth{} } func main() { msg := RegisterMessage["login"] t := reflect.TypeOf(msg).Elem() n := reflect.New(t).Interface().(IRoute) addroute(n) msg1 := RegisterMessage["auth"] t1 := reflect.TypeOf(msg1).Elem() n1 := reflect.New(t1).Interface().(IRoute) addroute(n1) }
package main
import (
"fmt"
)
/*****有点绕 哪里函数调用Run,其实就是执行哪个函数******/
type Example interface {
Run(int, string)
}
type ExampleHandle func(int, string)
func (f ExampleHandle) Run(i int, s string) {
f(i, s)
}
/***2个测试用的函数***/
func T1(i int, s string) {
fmt.Printf("T1: %d, %s\n", i, s)
}
func T2(i int, s string) {
fmt.Printf("T2: %d, %s\n", i, s)
}
func main() {
funcMap := make(map[string]Example)
funcMap["T1"] = ExampleHandle(T1)
funcMap["T2"] = ExampleHandle(T2)
f := funcMap["T1"]
f.Run(1, "aaa")
f = funcMap["T2"]
f.Run(2, "bbb")
}
}
根据动态字符串,匹配同名函数.或匹配结构体
package main import ( "errors" "fmt" "reflect" ) type Handler struct { } func (this *Handler) CallWithName(name string, args ...interface{}) error { value := reflect.ValueOf(this) method := value.MethodByName(name) if method == (reflect.Value{}) { return errors.New("not found") } if len(args) == 0 { method.Call(nil) } else { params := make([]reflect.Value, len(args)) for i, arg := range args { params[i] = reflect.ValueOf(arg) } method.Call(params) } return nil } func (this *Handler) Hello(name string) { fmt.Printf("Hello, %s!\n", name) } func (this *Handler) Hi() { fmt.Println("Hi!") } func main() { var handler Handler handler.CallWithName("Hello", "aaa") handler.CallWithName("Hi") handler.CallWithName("hia") }
未完待续

浙公网安备 33010602011771号