接口型函数
最近在写极客兔兔的GeeCache这个小项目,在里面遇到了一段不太理解的代码, 1234567891011type Getter interface { Get(key string) ([]byte, error)}// A GetterFunc implements Getter with a function.type GetterFunc func(key string) ([]byte, error)// Get implements Getter interface functionfunc (f GetterFunc) Get(key string) ([]byte, error) { return f(key)} 结合兔兔的讲解,写一篇博客 本质是实现多态 接口只有一个方法,为什么不直接使用GetterFunc作为参数传进参数? 12func GetFromSource(fn GetterFunc, key string)...
无标题
数据链路层
无标题
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠== You can decompress Drawing data with the command palette: ‘Decompress current Excalidraw file’. For more info check in plugin settings under ‘Saving’ Excalidraw Data Text...
GMP
线程 内核态,操作系统的最小调度单元,创建销毁,调度都需要由内核来完成,可以充分利用多核,实现并行 协程 用户态,是线程的子集,在线程的基础上,对线程进行二次加工得到,从属于某一个线程,与线程存在映射关系比例是m :...
主流排序算法
按照排序的依据,可以分为两类,比较类排序与非比较类排序 比较类排序是通过比较决定次序,快排,选择排序,插入排序都属于此类,比较类排序的优势是适用性,它不在乎数据的分布,只要能比较就能排,但是比较类排序的速度有上限,时间复杂度下界是O(NlogN)O(N logN)O(NlogN) 非比较类排序包括,计数排序,桶排序,基数排序,不通过排序进行比较,这类算法的时间复杂度为O(n)O(n)O(n),但是对数据类型限制比较大,而且因为用空间换时间,所以对数据的规模也有一定要求,此外,对数据的分布硬额比较敏感,如果分布不均匀(有的桶是空的,有的桶是满的),效率就会降低 冒泡排序 原理:依次比较两个元素,顺序错误就交换,直到没有交换的为止 代码实现: 12345678910111213141516171819func bubble(nums []int) []int { l := len(nums) //排序l - 1轮 for i := 0; i < l - 1; i++ { swapped := flase ...
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 //发送索引,下一次发送写入的位置 recvq uint //接受索引,下一次接收读取的位置 recvq waitq //双向链表,等待接收的goruntine队列 sendq waitq //等待发送的goruntine队列 lock mutex //互斥锁,保护结构体} recvq 和 sendq 的作用是存储那些“因为无法立即完成操作”而被迫阻塞(睡眠)的 Goroutine recvq:想读数据但读不到的 Goroutine sendq:想发数据但发不出去的...
包的匿名导入
作用:利用包的副作用,即执行包内的init函数,但是不直接使用该包导出的任何变量或函数 常见使用场景: 数据库注册驱动 12345678910111213 package mainimport ( "database/sql" "fmt" // 匿名导入 MySQL 驱动 // 我们代码里不需要直接写 mysql.Connect,所以用 _ "github.com/go-sql-driver/mysql")func main() { // 这里可以直接使用 mysql 驱动,因为上面的匿名导入已经把它注册进去了 db, err := sql.Open("mysql",...
Go语言圣经第9章-sync
不要使用共享数据来通信,使用通信来共享数据 无论任何时候,只要有两个goroutine并发访问同一变量,且至少其中的一个是写操作的时候就会发生数据竞争 避免数据竞争的三种方式: 不去’写’变量 避免多个goroutine访问变量,变量都被限定在一个goroutine中,...
延迟双删
sbCSDN,教的东西是错的,我对着绕了好一会儿 旁路缓存 流程: 写策略:先更新数据库里的数据,再删除缓存 读策略:如果命中缓存,直接返回数据,没有命中,则从数据库中读取,然后将数据写入缓存,再返回数据 缺点:存在极低概率的并发问题(读操作比写操作慢得多时,可能导致旧数据覆盖),且严重依赖“删除缓存”这一步的成功 延迟双删 流程:删除缓存->更新数据库->休眠一会儿->再次删除数据 第二次删是解决在数据库更新中,其他线程读取到旧数据并写入缓存的问题,中间需要延迟的原因是等待读取了旧数据的线程把缓存写入 既然数据库在更新过程中,其他线程读取的是旧数据,最后都需要第二次删除,那我为什么不留着缓存,这样还能减少数据库的压力 第一次删除实际是把读旧缓存变成了 读旧数据库 +...






