当前位置: 首页 > news >正文

【基础】go进阶学习笔记

匿名方法
  • 匿名方法写法:func (x){...}(),不用指定名称,所以叫匿名方法
  • 匿名方法其实类似于一个类型实例,匿名方法声明后可以用来赋值和传参,也可以直接使用()运行
结构体标签
  • 为结构体的字段携带元数据的方式,使用反引号包裹,定义为字符串字面量,支持在字段后一次定义多个,空格隔开
  • go编译器不会对其进行额外转义等处理,而go运行时本身也不会使用该内容,而是第三方库在通过反射等方式进行使用
  • 定义方式是在结构体的具体变量后面,对字段进行描述的场景下尤其有用,写法如下:
type User struct {// 导出为 JSON 时是 "first_name",导出为 YAML 时是 "firstName"// 并不是说后续使用时go运行时会自动帮助转换,而是被三方库解析和处理使用FirstName string `json:"first_name" yaml:"firstName"`
}
  • 常用约定是反引号内的格式通常是k:v的形式,即冒号隔开的键值对,因为元数据的表达方式通常也是键值对,所以k:v这种形式的结构体标签会很常见
go协程和信道
go协程
  • 官方说法是:由 Go 运行时管理的轻量级线程
  • 启动一个协程:go f(x, y, z), 注意:f, x, yz 的求值发生在当前的 Go 协程中,而 f 的执行发生在新的 Go 协程中 ,理解这句话很重要,只有f函数的代码执行被放在了新协程中,而参数的求值和响应值求值(没错,响应值返回出来时也会求值)都发生在当前go协程中
  • 不同协程间的通信使用信道完成,可以避免其他语言中复杂的锁机制
信道
  • 信道是用来在不同的协程之间通信的,其概念类似于一个数据传送带,遵循先进先出
  • 箭头方向表达了数据流的方向
  • 将值发送到信道:ch <- v
  • 从信道接收值:v := <-ch,操作符是紧挨着信道值的
  • 信道使用make函数创建:ch := make(chan T),chan关键字修饰一个类型,表示该信道只为该类型工作,不允许放其他类型值
  • 信道操作符只有:<-,不存在其他方式比如-> -<之类的
  • 信道最重要的特点是内置同步:无缓冲信道的话,接收端如果没有在接收(可能阻塞了),那么发送端也不会进行实际发送操作,也会阻塞等待,直到接收端重新开始接收。有缓冲信道的话也是会发满缓冲区,就不再继续发送了,直到缓冲区再次有新的空间
  • 信道分两种:无缓冲信道:ch := make(chan T),有缓冲信道:ch := make(chan T, 5)
  • 有缓冲的信道就相当于发送端和接收端中间放了一个操作台用来中转数据,即使接收端阻塞了,发送端也会发送直到发满数据到缓冲区(这里是5条数据)。
  • 有缓冲信道,当信道的缓冲区填满后,向其发送数据时会阻塞,当缓冲区为空时,接受方会阻塞
  • 信道还有进阶的用法,可以作为函数的参数声明时,限制函数体内只能发还是只能接:
//这个函数只能向信道发送数据,不能接收 
func sendData(ch chan<- string, data string) { ch <- data }//这个函数只能从信道接收数据,不能发送
func receiveData(ch <-chan string) { fmt.Println(<-ch) }
  • 信道缓冲区如果满了,再直接发送就会报错:
package mainimport "fmt"func main() {ch := make(chan int, 2)ch <- 1ch <- 2ch <- 3 //放开这个注释,运行时就会报错fmt.Println(<-ch)fmt.Println(<-ch)
}//报错:
fatal error: all goroutines are asleep - deadlock!
  • 信道缓冲区如果没数据,但是接收端却在主动发起接收,就会报错
package mainimport "fmt"func main() {ch := make(chan int, 2)ch <- 1//ch <- 2 //注释这一行后,运行时也会报错fmt.Println(<-ch)fmt.Println(<-ch)
}//报错:
1
fatal error: all goroutines are asleep - deadlock!
  • 接收者端可以使用双重赋值来接受第二个参数来检查信道是否已被关闭:
v, isOpen := <-ch
  • 关闭信道只允许发送者来关闭,接收者发起关闭的话会导致panic
  • range可以不断的从信道中取值,直到信道被关闭
  • 信道并不是必须要关闭的,只有在需要告诉接收者不再有需要接收的值是才有必要关闭,例如停止一个range的循环
  • 一段标准的协程演示代码,包含信道创建、遍历、关闭用法:
package mainimport ("fmt"
)func fibonacci(n int, c chan int) {x, y := 0, 1for i := 0; i < n; i++ {c <- xx, y = y, x+y}close(c)
}func main() {c := make(chan int, 10)go fibonacci(cap(c), c)for i := range c {fmt.Println(i)}
}
  • select语句写法类似switch,主要作用是可以使一个协程可以等待多个通信操作。
  • select是哪个分支就绪就执行哪个,都就绪则会随机选择一个分支执行
  • select写法:
select {case c <- x:x, y = y, x+ycase <-quit:fmt.Println("quit")return}
  • select 的分支都没有准备好时,就会运行default分支,使用default分支可以避免发送或接收时产生阻塞
  • select示例代码,有效说明了select常规的工作机制:
package mainimport ("fmt""time"
)func main() {tick := time.Tick(100 * time.Millisecond)boom := time.After(500 * time.Millisecond)for {select {case <-tick:fmt.Println("tick.")case <-boom:fmt.Println("BOOM!")returndefault:fmt.Println("    .")time.Sleep(50 * time.Millisecond)}}
}//打印结果:. .
tick...
tick...
tick...
tick...
tick.
BOOM!
  • 运用信道完成等价二叉树的遍历和检查:
package mainimport ("fmt""golang.org/x/tour/tree"
)// 递归的中序遍历函数
func Walk0(t *tree.Tree, ch chan int) {if t == nil {return}Walk0(t.Left, ch)ch <- t.ValueWalk0(t.Right, ch)
}// Walk 遍历树 t,并树中所有的值发送到信道 ch。
func Walk(t *tree.Tree, ch chan int) {Walk0(t, ch)close(ch)
}// Same 判断 t1 和 t2 是否包含相同的值。
func Same(t1, t2 *tree.Tree) bool {//创建两个管道同时接收值,并且在循环中同时取值ch1 := make(chan int)ch2 := make(chan int)go Walk(t1, ch1)go Walk(t2, ch2)for {a, ok1 := <-ch1b, ok2 := <-ch2//如果没有同时结束或者a不等于b,就说明结构或值有不同的地方,直接返回falseif ok1 != ok2 || a != b {return false}if ok1 == false {break}}return true}func main() {ch := make(chan int)go Walk(tree.New(1), ch)for item := range ch {fmt.Println(item)}t1 := tree.New(1)t2 := tree.New(1)t3 := tree.New(2)fmt.Printf("t1 和 t2 是否相同:%v\n", Same(t1, t2))fmt.Printf("t1 和 t3 是否相同:%v\n", Same(t1, t3))
}
  • 互斥锁使用sync的sync.Mutex实现,只需要对应的结构体持有这个锁实例就能实现并发安全的访问:mu sync.Mutex,因为库的缘故,实际上无需初始化就能使用。在函数中上锁时调用 mu.Lock(),解锁时使用mu.Unlock(),配合defer关键字来保证该锁一定会被解锁
http://www.dtcms.com/a/319166.html

相关文章:

  • Android渲染/合成底层原理详解
  • B 站 SEO 优化全景指南:从基础到进阶的实操方法
  • 贪心+矩阵算法
  • Oracle 关闭 impdp任务
  • 云原生安全挑战与治理策略:从架构思维到落地实践
  • 基于大数据的美食视频播放数据可视化系统 Python+Django+Vue.js
  • 解读 gpt-oss-120b 和 gpt-oss-20b开源模型
  • 仓库管理系统-20-前端之记录管理的联表查询
  • Android中视图测量、布局、绘制过程
  • 嵌入式 - 数据结构:二叉树
  • GitHub 上 Star 数量前 20 的开源 AI 项目
  • X4000 私有 5G 实验室入门套件
  • 90-基于Flask的中国博物馆数据可视化分析系统
  • MySQL的变量、控制流程和游标:
  • 智能升级新纪元:基于Deepoc具身模型外拓开发板的除草机器人认知进化
  • git工程多个remote 拉取推送
  • 配置VScode内置Emmet自动补全代码
  • leetcode 415.字符串相加
  • 如何重塑企业服务体验?
  • 六边形架构模式深度解析
  • 深度学习(1):pytorch
  • SurgRIPE 挑战赛:手术机器人器械位姿估计基准测试|文献速递-医学影像算法文献分享
  • Next.js 样式:CSS 模块、Sass 等
  • 前端技术架构设计文档(Vue2+Antd+Sass)
  • 安全合规2--网络安全等级保护2.0介绍
  • A Logical Calculus of the Ideas Immanent in Nervous Activity(神经网络早期的M-P模型)
  • Spring Boot整合PyTorch Pruning工具链,模型瘦身手术
  • 记录一次Inspur服务器raid配置流程
  • 【数据库】如何从本地电脑连接服务器上的MySQL数据库?
  • 某梆企业壳frida检测绕过