协程栈 go栈的位置 1.Go协程栈位于Go-堆内存上 2.Go堆内存位于操作系统虚拟内存上 go栈的工作流程 以main.main为出发点 要记录runtime.main的栈基地址 记录a和b的局部变量值 开辟一个空间记录sum函数的返回值 记录b和a的值,这里是为了方便sum在执行时候,去找这个传入的参数 记录sum返回后,要执行的指令,就是print的执行位置. 这里面能有一个重要体现,a和b的值被复制了一份,一般称为拷贝传递或值传递 传递结构体时:会拷贝结构体中的全部内容. 传递结构体指针时:会拷贝结构体指针 小结协程栈记录的内容: 协程的执行路径 局部变量 函数...

  BkylWvL1Vjn2   2023年12月04日   25   0   0 Go

垃圾回收(GarbageCollecting)思路 1."标记-清除〞go的做法 2."标记-整理〞标记后删除,删除后重新把内存空间整理java早期 3."标记复制〞两块相似的内存,直接把有用的拷贝到另外一块上去java现阶段 Go因为堆内存结构的独特优势,选择最简单的标记-清除 找到有引用的对象,剩下的就是没有引用的 如何标记 有用的对象从哪里开始找 1.被栈上的指针引用一块堆上对象,现在被栈上指针引用了,栈上是正则运行的程序. 2.被全局变量指针引用 3.被寄存器中的指针引用当做参数或者中间变量,放入了寄存器中,也是正则运行的 上述变量被称为RootSet(GCRoot) ...

  BkylWvL1Vjn2   2023年12月04日   24   0   0 Go

网络基础 协议架构 tcp链接 假如需要开发者去实现一套新的网络协议(例如redis的resp),是基于TCP的,那tcp这层的协议,是否需要开发者自己去实现? 这层如果自己实现,其实很复杂,会涉及很多算法相关. 因此,出现了socket对传输层进行了抽象,开发者不需要关注传输层具体的实现,使用socket提供的接口,socket内部会实现,比如三次握手,四次挥手. Socket 很多系统都提供Socket作为TCP(也有UDP)网络连接的抽象, Linux->Internetdomainsocket->SOCKSTREAM Linux中Socket以“文件描述符〞FD...

  BkylWvL1Vjn2   2023年12月04日   29   0   0 Go

go读写锁 互斥锁每次只让一g通过,去读写数据。但是读数据操作,并发其实没有问题。所以诞生了读写锁。 读协程可以并发,一起读。但是写协程还是要走互斥锁,只能一个个通过。 先加了读锁 先加了读锁。那么写的协程,就需要去休眠队列中等待。一直到读锁都释放。 先加了写锁 这个时候,不管再来写协程还是读协程,都去休眠队列等待。 小结: 没有加写锁时,多个协程都可以加读锁 加了写锁时,无法加读锁,读协程排队等待 加了读锁,写锁排队等待 定义 typeRWMutexstruct{ wMutex//heldiftherearependingwriters writerSemuint32//semap...

  BkylWvL1Vjn2   2023年12月03日   23   0   0 Go

模型 三部分组成: 发送等待队列 接收等待队列 管道缓存区 定义 在runtime的chan.go中 typehchanstruct{ qcountuint dataqsizuint bufunsafe.Pointer elemsizeuint16 elemtype_type//elementtype //上面的这几个组成了一个环形缓存区 closeduint32//关闭状态 //下面4个组成了2个队列g的 sendxuint//sendindex recvxuint//receiveindex recvqwaitq//listofrecvwaiters sendq...

  BkylWvL1Vjn2   2023年12月03日   25   0   0 Go

互斥锁的定义 typeMutexstruct{ stateint32 semauint32 } 一个sema,背后实际上是一个休眠队列,可以看下上篇。一个state,这个状态分为4个部分。后三位各自代表一个状态。前29位代表最大可等待协程的个数。 state的结构 locked是否加锁1加锁,0正常占1位 woken是否醒来占1位 starving是否饥饿模式占1位 waiterShift等待的数量占29位 底层的定义,下面看代码时候,会说明。 正常模式 加锁 假设现在来了2个g,都想加锁,但是只有一个能成功,2个都通过atomic.CompareAndSwapInt32(lo...

  BkylWvL1Vjn2   2023年12月01日   28   0   0 Go

go的协程和线程都绕不过GMP,关于GMP基本的工作流程,有go开发经验的大致都懂,这边更多关注GMP如何解决一些类似协程饥渴的问题,以及底层的大致实现原理。 多线程循环 上篇讲了单线程是如何循环的,这里还是为GMP的出场大致介绍下。 工作模型 多个M都去全局G的队列中获取g,所以,全局g的队列需要上锁。 改进版,增加本地队列 这样每个m都缓存了一个本地队列,避免每次都去全局队列里面拿,而且一次也能拿多个,降低了全局队列锁获取的开销。 这个其实就是GMP了。P指的就是为M管理要执行的g P的定义 在runtime2.go中有定义: 删除了很多源码,只留下和这里讲的相关的代码: ...

  BkylWvL1Vjn2   2023年12月01日   30   0   0 Go

当m在执行某个g的时候,g非常耗时,例如一个for循环,每次循环sleep1分钟,循环1000次。 这个例子看似无聊,却是很难解决的,成功的避开了2个系统切换时机。 如果这个时候,一直执行这个g,别的g就会得不到执行,例如有g是处理用户支付的,这样就会造成收钱不积极。 协程饥饿问题 本地队列 本地队列因为某个G一直占着M,导致其他G无法执行。 如果占用时间过长的这个G,能让出来M,让别的G也能执行,本地队列循环的着执行,就能解决这个问题。 全局队列 除了本地队列,全局队列也会有这个问题,如果一个新创建的g,放在全局队列中,而现有的p的本地队列都未执行完,则全局队列需要排队很久。...

  BkylWvL1Vjn2   2023年12月01日   28   0   0 Go

atomic和sema是实现go中锁的基础,简单看下他们的实现原理。 atomic atomic常用来作为保证原子性的操作。 当多个协程,同时一个数据进行操作时候,如果不加锁,最终的很难得到想要的结果。 varpint64=0 funcadd(){ p=p+1 } funcmain(){ fori:=0;i<1000;i{ goadd() } time.Sleep(time.Second5) fmt.Println(p)//982 } 这种情况下,最终打印的都不会是1000,每次不固定。 改成atomic能解决 varpint64=0 funcadd(){ atom...

  BkylWvL1Vjn2   2023年12月01日   34   0   0 Go

切片的底层数据结构 有上篇string为基础了,能猜到slice肯定也有一个对应的struct。 在runtime的slice.go中 typeslicestruct{ arrayunsafe.Pointer lenint capint } 切片的本质是对数组的引用 len表示当前已经存储的个数,cap表示容量。 切片的创建 根据数组创建s:=arr[0:31] 字面量:编译时插入创建数组的代码s:=[]int{1,2,3} make:运行时创建数组slice:=make([lint,10) 切片的追加 扩容时,编译时转为调用runtime.growslice()有兴趣的可...

  BkylWvL1Vjn2   2023年12月01日   34   0   0 Go

go的map在面试时候经常会被问到。 最近看到群里有个被问到为什么map的每个桶中只装8个元素? map的结构 注:解决hash冲突还有一些别的方案:开放地址法(往目标地址后面放)、再哈希法(再次hash) 底层定义 //AheaderforaGomap. typehmapstruct{ //个数sizeofmap,当使用len方法,返回就是这个值 countint//livecellssizeofmap.Mustbefirst(usedbylen()builtin) flagsuint8 //桶的以2为底的对数,后面在查找和扩容都用到这个值 Buint8//log_2of...

  BkylWvL1Vjn2   2023年12月01日   28   0   0 Go

定义 在runtime的sync.map包中有定义: typeMapstruct{ muMutex//锁 readatomic.Pointer[readOnly]//包含了readOnly类型的一个struct,下方把Pointer也贴了 dirtymap[any]entry //一个map存储数据 missesint//错过、没有命中 } //readOnlyisanimmutablestructstoredatomicallyintheMap.readfield. typereadOnlystruct{ mmap[any]entry//一个map,这个map是read持有的 ...

  BkylWvL1Vjn2   2023年12月01日   42   0   0 Go

空结构体 funcmain(){ a:=struct{}{} fmt.Println(unsafe.Sizeof(a)) fmt.Printf("%p\n",&a) } 打印 0 0x117f4e0 有经验的开发人员都知道,所有的空结构体是指向一个zerobase的地址,而且大小为0 一般用来作结合map作为set或者在channel中传递信号。 typevoidstruct{} typevoid1struct{ avoid } typevoid2struct{ avoid bint } funcmain(){ a0:=void{} a1:=void1{} a...

  BkylWvL1Vjn2   2023年12月01日   24   0   0 Go

协程与线程 线程在创建、切换、销毁时候,需要消耗CPU的资源。 协程就是将一段程序的运行状态打包,可以在线程之间调度。减少CPU在操作线程的消耗 协程、线程、进程这块网上非常多文章讲了,就不多叙述了。 归纳下: 进程用分配内存空间 线程用来分配CPU时间 协程用来精细利用线程 协程的本质是一段包含了运行状态的程序后面介绍后,会对这个概念更好理解 协程的本质 上面讲了,协程的本质就是一段程序的运行状态的打包: funcDo(){ fori:=1;i<=1000;i{ fmt.Println(i) time.Sleep(time.Second) } } funcmain(){...

  BkylWvL1Vjn2   2023年12月01日   33   0   0 Go
关注 更多

空空如也 ~ ~

粉丝 更多

空空如也 ~ ~