Go1.27
泛型 之前如果想要为一个非泛型结构体是编写一个可以处理任意类型的泛型方法, 那只能把这个方法声明称一个包级函数, 像这样 12345type Converter struct {}func ConvertToStrng[T any](c Converter, val T) string { return fmt.Sprintf("%v", val)} 而现在, 方法可以声明自己的类型参数了, 泛型函数可以约束在特定的数据类型中使用 123456type Converter struct{}//方法现在可以有自己的类型参数Tfunc (c Converter) ConverterToString[T any](val T) string {} 嵌套结构体初始化 以前 12345678type Address struct { City string}type User struct { Name string ...
defer
defer后面如果是普通的函数调用, 那函数的参数在执行到defer的时候就已经确定了 defer后面如果是闭包, 闭包内部直接使用外部变量, 那么真正执行这个闭包的时候才会读取变量的当前值 执行到defer的时候, defer函数调用的函数值和参数会立刻求值并保存, 函数体延迟到当前函数返回前执行 12345678910func main() { fmt.Println(test1()) fmt.Println(test2()) fmt.Println(test3()) fmt.Println(test4())}func test1() (v int) { defer fmt.Println(v) return v} 进入defer的时候, Println没有执行, 但是v已经确定了 打印0 0 123456func test2() (v int) { defer func() { fmt.Println(v) }() return...
panic
panic用于表示程序无法继续运行的严重错误 panic使用defer + recover()函数进行捕获, recover只能在defer调用的函数中生效 recover只能捕获同一个goroutinue的panic 123456789101112func main() { defer func() { fmt.Println(recover()) }() go func() { panic("goroutine panic") }() select {}} 这里就捕获不到gouroutinue panic这个错误 原因就是recover在main, 而panic发生在别的goroutinue 这样就可以捕获 1234567891011121314151617func run() { defer func() { if err := recover(); err != nil...
map底层原理
1.24以前使用的是hmap, 在1.24及之后使用的是swisstable 先看一下老版本的hmap hmap 12345678910111213type hmap struct { count int flags uint8 B uint8 noverflow uint16 hash0 uint32 buckets unsafe.Pointer oldbuckets unsafe.Pointer nevacuate uintptr extra *mapextra} 可以理解成 12345678910111213141516map 变量 | v hmap | +--> buckets ------> 当前 bucket 数组 | +--> oldbuckets ---> 扩容前的 bucket 数组 | +--> count 元素数量 | +--> B ...
单例模式
适用场景 一些实例化很耗费资源的类, 比如线程池, 协程池 一些只允许存在一个实例的类, 比如日志管理模块 实现方式 饿汉式 一开始就完成单例的初始化 实现流程 12345678910111213141516171819var s *demofunc init() { s = newDemo()}type demo struct {}func newDemo() *demo { return &demo{}}func (s *demo)Work { }func GetDemo() *demo { return s} 代码规范性讨论 demo是包内不可导出的类型, 在包内获取到了, 也不能作为入参或者出参进行传递 demo对外暴露, 混淆了不可导出类的边界与定位 处理方法 在不可导出单例类基础上 包上一层interface, 然后将这个inerface作为返回参数类型 123456type Instace interface...
计算机网络-网络层
网络层 湖科大计网 分类编址的IPv4地址 A类地址 最小网络号为0, 保留不指派 第一个可指派的网络号为1, 网络地址为1.0.0.0 最大网络号为127, 作为本地环回测试地址,不指派 最小的本地环回测试地址为127.0.0.1 最大的本地环回测试地址为127.255.255.254 最后一个可指派的网络号为126 可指派的网络的数量为126, 每个网络中可以分配的IP地址数量为2^24 - 2 = 16777214 (减2是取出全0的网络地址和全1的广播地址) B类 最小网络号128, 最大网络号191.255 可指派网络数为2 ^ (16 - 2) = 16384, 每个网络中可分配的IP地址数量为2 ^ 16 - 2 = 65534 C类 最小网络号192, 最大网络号223.255.255 可指派的C类网络数量2 ^(24 - 3), 每个网络中可分配的IP地址数量为2 ^ 8 -...
GMP
线程 内核态,操作系统的最小调度单元,创建销毁,调度都需要由内核来完成,可以充分利用多核,实现并行 协程 用户态,是线程的子集,在线程的基础上,对线程进行二次加工得到,从属于某一个线程,与线程存在映射关系比例是m :...
context原理
这篇文章讲的太好了,自己记录一下加深印象 主要用途:用于在异步场景并发协调以及对goruntinue的生命周期进行控制,传递取消信号,超时,截止时间,并且具有一定的数据存储能力 核心数据结构 123456type Context interface { Deadline() (deadline time.Time, ok bool)//返回context的过期时间 Done() <-chan struct{}//返回一个channel,当Context被取消或超时,这个channel会关闭 Err() error//返回错误 Value(key any) any//在调用链中携带的键值对数据} emptyCtx 根基:是所有context的根 类的实现 12345678type emptyCtx intfunc (*emptyCtx)Deadline() (deadline time.Time, ok bool) {return}func (*emptyCtx) Done()...
channel底层实现
数据结构 Channel在运行时使用hchan结构体,结构如下 12345678910111213141516type hchan struct { qcount uint //队列中当前元素的个数 dataqsiz kuint //环形队列的总容量 buf unsafe.Pointer //指向底层环形数组的指针 elemsize uint16 //元素的大小 elemtype *_type //元素的类型信息 sendx uint //发送索引,下一次发送写入的位置 recvx uint //接受索引,下一次接收读取的位置 recvq waitq //双向链表,等待接收的goruntine队列 sendq waitq //等待发送的goruntine队列 lock mutex //互斥锁,保护结构体} recvq 和 sendq 的作用是存储那些“因为无法立即完成操作”而被迫阻塞(睡眠)的 Goroutine recvq:想读数据但读不到的 Goroutine sendq:想发数据但发不出去的...
延迟双删
旁路缓存 流程: 写策略:先更新数据库里的数据,再删除缓存 读策略:如果命中缓存,直接返回数据,没有命中,则从数据库中读取,然后将数据写入缓存,再返回数据 缺点:存在极低概率的并发问题(读操作比写操作慢得多时,可能导致旧数据覆盖),且严重依赖“删除缓存”这一步的成功 延迟双删 流程:删除缓存->更新数据库->休眠一会儿->再次删除数据 第二次删是解决在数据库更新中,其他线程读取到旧数据并写入缓存的问题,中间需要延迟的原因是等待读取了旧数据的线程把缓存写入 既然数据库在更新过程中,其他线程读取的是旧数据,最后都需要第二次删除,那我为什么不留着缓存,这样还能减少数据库的压力 第一次删除实际是把读旧缓存变成了 读旧数据库 +...






