• Go基础学习【3】


    一:接口

    定义:每个接口由人一个签名构成
    命名规范:接口类型名后以er结尾 方法名首字母大写

    type 接口名  interface
    
    • 1

    使用接口避免了大量的重复和代码的冗余, 接口的实现:实际问题中,很多东西都可以归为一类接口中来实现

    1.值接收者 和 指针接收者 实现接口
    值接收者:不管结构体的值接受者还是指针接收者变量 都可以赋值给接口变量

    ype Mover interface {
       Move()
    }
    // Cat 猫结构体类型
    type Cat struct{}
    // Move 使用指针接收者定义Move方法实现Mover接口
    func (c *Cat) Move() {
       fmt.Println("猫会动")
    }
    func main() {
       var x Mover
       var c1 = &Cat{}
       x = c1
       x.Move()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    存在多个接口组成一个新接口 | 接口作为结构体中字段出现
    2.空接口
    声明:直接声明

    var x interface{}
    
    • 1

    空接口可以实现和存储任意类型的值
    接收任意类型的函数参数
    保存任意值的字典

    3.接口值:分为类型type 和 值values ,通过指针赋值的话

    type Mover interface {
    Move()
    }
    type Dog struct {
    Name string
    }
    func (d *Dog) Move() {
    fmt.Println("狗在跑~")
    }
    type Car struct {
    Brand string
    }
    func (c *Car) Move() {
    fmt.Println("汽车在跑~")
    }
    func main(){
         var m Mover //首先创建一个Mover接口类型的变量m 此时它的值和类型都是空
         //同时空接口值不能进行方法的调用
         m = &Dog{Name: "旺财"}
         //通过结构体指针赋值,此时m的类型为*DOg 值为Dog{“旺财}
         vat c *Car
         m = c
         //此时类型为*Cat,值为空   因为只有值为空,所以此时m != nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    二:反射

    对程序本身进行访问和修改的能力
    使用reflect进行反射,任意接口值在反射都可以理解为
    reflect.Type 和 reflect.Value

    在反射中,类型分为 type类型和 kind种类两种
    在区分指针 结构体时 需要用到kind
    在反射中,使用特定函数Elem()来获取指针地址来改变变量值

    if v.Elem().Kind() == reflect.Int64 {
    v.Elem().SetInt(200)
    }
    
    • 1
    • 2
    • 3

    IsNil() 常判断指针是否是空 IsValid()常用过来判断返回值是否有效

    var a *int
    fmt.Println("var a *int IsNil:", reflect.ValueOf(a).IsNil())
    fmt.Println("nil IsValid:", reflect.ValueOf(nil).IsValid())
    
    • 1
    • 2
    • 3

    三:并发

    为了加快程序运行的速度而引入

    串行:按照顺序 并发:某时间段内执行多个任务
    并行:某时刻内执行多个任务
    进程:程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位
    线程:轻量级进程
    协程:比线程更轻量级

    在go语言中 这些都不需要自己去实现,调用goroutine即可自动实现
    会输出你好 不会输出hello,因为主函数main是一个goroutine 主函数内又创建了一个foroutine,因为创建需要一定时间,所以主函数先运行,先结束 随着主函数结束 ,方法hello不再输出

    func hello() {
    fmt.Println("hello")
    }
    func main() {
    go hello()
    fmt.Println("你好")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用下面的方法:当创建一个新的goroutine的时候,会进行计数,完成后会进行反馈,主函数结束时,被卡着,当收到反馈的信息时,才进行

    // 声明全局等待组变量
    var wg sync.WaitGroup
    
    func hello() {
    fmt.Println("hello")
    wg.Done() // 告知当前goroutine完成
    }
    
    func main() {
    wg.Add(1) // 登记1个goroutine
    go hello()
    fmt.Println("你好")
    wg.Wait() // 阻塞等待登记的goroutine完成
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述
    channel 将一个goroutine与另一个goroutine通信

    var ch1 chan int   // 声明一个传递整型的通道
    var ch2 chan bool  // 声明一个传递布尔型的通道
    var ch3 chan []int // 声明一个传递int切片的通道
    
    • 1
    • 2
    • 3

    声明通道类型的变量需要使用内置的make函数进行初始化

    ch4 := make(chan int)
    ch5 := make(chan bool, 1)  // 声明一个缓冲区大小为1的通道
    
    • 1
    • 2

    通道有:send receive close 三种操作 发送和接收都使用<-符号
    send

    ch <- 10 // 把10发送到ch中
    
    • 1

    close:

    close(ch)
    
    • 1

    无缓冲通道:使用另一个goroutine来接收通道传过来的值

    func recv(c chan int) {
    ret := <-c
    fmt.Println("接收成功", ret)
    }
    
    func main() {
    ch := make(chan int) /创建的是无缓冲通道
     //解决方法一 
    go recv(ch) // 创建一个 goroutine 从通道接收值
    ch <- 10
    fmt.Println("发送成功")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    有缓冲的通道:为了解决无缓冲通道时出现的死锁,可以创建的时候通过make来规定通道的容量

    func main() {
    ch := make(chan int, 1) // 创建一个容量为1的有缓冲区通道
    ch <- 10
    fmt.Println("发送成功")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    通过for range来输出通道的值
    单向通道
    为了使某一个方法函数只具有本功能,而不具有其他功能,以此来避免不必要的破坏

    <- chan int // 只接收通道,只能接收不能发送
    chan <- int // 只发送通道,只能发送不能接收
    
    • 1
    • 2

    并且默认的通道关闭操作应该是由发送方来实现的
    select语句:处理一个或多个channel的发送、接收操作 如果多个case同时满足,会随机选一个 ,对于没有case的select,会一直阻塞
    下面这个例子会输出10以内的奇数,因为当i=1时,因为此时通道里面没有值,所以select会执行将1写进去,当i=2时,此时容量已经满了,所以处输出1,之后以此类推

    package main
    
    import "fmt"
    
    func main() {
    ch := make(chan int, 1)
    for i := 1; i <= 10; i++ {
    select {
    case x := <-ch:
    fmt.Println(x)
    case ch <- i:
    }
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    互斥锁:在同一时间内,只能有一个goroutine访问共享资源 sync.Mutex 类型来实现互斥锁
    下面例子避免了资源竞争

    // add 对全局变量x执行5000次加1操作
    func add() {
    for i := 0; i < 5000; i++ {
    m.Lock() // 修改x前加锁
    x = x + 1
    m.Unlock() // 改完解锁
    }
    wg.Done()
    }
    func main() {
    wg.Add(2)
    
    go add()
    go add()
    
    wg.Wait()
    fmt.Println(x)
    } //10000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    读写互斥锁 :可以大大提高效率,节省时间(适用场景:读多写少)
    原子操作:也可以保证并发安全性 同时比锁的效率更高 sync/atomic

    详细查看:我的笔记链接
    感谢观看

  • 相关阅读:
    PCB设计---深入浅出的反弹图
    [Linux](13)system V 共享内存
    抑郁了怎么办?改变抑郁情绪的几个建议
    undo log
    Dockerfile关键词
    数据科学必备Python编程基础
    【K8s入门必看】第二篇 —— 快速部署集群指南
    为什么UI自动化难做?—— 关于Selenium UI自动化的思考
    3d 舞蹈同步
    springboot基础入门
  • 原文地址:https://blog.csdn.net/weixin_54174102/article/details/127984790