Go基础学习【3】

简介: Go基础学习【3】

一:接口

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

type 接口名  interface

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


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

存在多个接口组成一个新接口 | 接口作为结构体中字段出现


2.空接口

声明:直接声明

var x interface{}

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


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
}


二:反射

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

reflect.Type 和 reflect.Value

在反射中,类型分为 type类型和 kind种类两种

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

if v.Elem().Kind() == reflect.Int64 {
v.Elem().SetInt(200)
}


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

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


三:并发

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

串行:按照顺序 并发:某时间段内执行多个任务

并行:某时刻内执行多个任务

进程:程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位

线程:轻量级进程

协程:比线程更轻量级


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

func hello() {
fmt.Println("hello")
}
func main() {
go hello()
fmt.Println("你好")
}


使用下面的方法:当创建一个新的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完成
}

c0f263b5a58f41afa2ab4863f68656df.png



channel 将一个goroutine与另一个goroutine通信

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


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

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


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

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


close:

close(ch)


无缓冲通道:使用另一个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("发送成功")
}


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

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


通过for range来输出通道的值

单向通道

为了使某一个方法函数只具有本功能,而不具有其他功能,以此来避免不必要的破坏

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


并且默认的通道关闭操作应该是由发送方来实现的

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


互斥锁:在同一时间内,只能有一个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

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

相关文章
|
3月前
|
程序员 Go 云计算
2023年学习Go语言是否值得?探索Go语言的魅力
2023年学习Go语言是否值得?探索Go语言的魅力
|
3月前
|
缓存 NoSQL Go
通过 SingleFlight 模式学习 Go 并发编程
通过 SingleFlight 模式学习 Go 并发编程
|
9天前
|
数据采集 监控 Java
go语言编程学习
【11月更文挑战第3天】
27 7
|
19天前
|
设计模式 测试技术 Go
学习Go语言
【10月更文挑战第25天】学习Go语言
19 4
|
2月前
|
编译器 Go
go语言学习记录(关于一些奇怪的疑问)有别于其他编程语言
本文探讨了Go语言中的常量概念,特别是特殊常量iota的使用方法及其自动递增特性。同时,文中还提到了在声明常量时,后续常量可沿用前一个值的特点,以及在遍历map时可能遇到的非顺序打印问题。
|
3月前
|
JSON 中间件 Go
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
本文详细介绍了如何在Go项目中集成并配置Zap日志库。首先通过`go get -u go.uber.org/zap`命令安装Zap,接着展示了`Logger`与`Sugared Logger`两种日志记录器的基本用法。随后深入探讨了Zap的高级配置,包括如何将日志输出至文件、调整时间格式、记录调用者信息以及日志分割等。最后,文章演示了如何在gin框架中集成Zap,通过自定义中间件实现了日志记录和异常恢复功能。通过这些步骤,读者可以掌握Zap在实际项目中的应用与定制方法
131 1
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
|
3月前
|
算法 NoSQL 中间件
go语言后端开发学习(六) ——基于雪花算法生成用户ID
本文介绍了分布式ID生成中的Snowflake(雪花)算法。为解决用户ID安全性与唯一性问题,Snowflake算法生成的ID具备全局唯一性、递增性、高可用性和高性能性等特点。64位ID由符号位(固定为0)、41位时间戳、10位标识位(含数据中心与机器ID)及12位序列号组成。面对ID重复风险,可通过预分配、动态或统一分配标识位解决。Go语言实现示例展示了如何使用第三方包`sonyflake`生成ID,确保不同节点产生的ID始终唯一。
go语言后端开发学习(六) ——基于雪花算法生成用户ID
|
3月前
|
Go
Go - 学习 grpc.Dial(target string, opts …DialOption) 的写法
Go - 学习 grpc.Dial(target string, opts …DialOption) 的写法
58 12
|
3月前
|
JSON 缓存 监控
go语言后端开发学习(五)——如何在项目中使用Viper来配置环境
Viper 是一个强大的 Go 语言配置管理库,适用于各类应用,包括 Twelve-Factor Apps。相比仅支持 `.ini` 格式的 `go-ini`,Viper 支持更多配置格式如 JSON、TOML、YAML
go语言后端开发学习(五)——如何在项目中使用Viper来配置环境
|
2月前
|
Rust Linux Go
Rust/Go语言学习
Rust/Go语言学习