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. *和&的区别 :

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

 

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

 

 

 未完待续

posted @ 2022-03-31 14:24  韩增  阅读(101)  评论(5)    收藏  举报