【go的源码阅读】context的实现:context.go

理解Context 这篇文章介绍的很清楚:深入理解Go Context 这个比较详细,但是层次不好:理解GO CONTEXT机制 关于context的使用场景 context的主要使用场景在于:一个任务在处理的过程中可能会启动很多个协程来进行处理。在这个过程中,如果上游的任务想要取消,下游的任务也应当一起取消。context的任务就来了。 内容介绍 context包的内容可以概括为:1个接口,4个实现,6个方法 接口 context.Context 一个接口是指:context.Context type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} } Deadline( ) Deadline会返回一个超时时间,Goroutine获得了超时时间后,例如可以对某些io操作设定超时时间。 函数签名 Deadline() (deadline time.Time, ok bool) Deadline 返回的时间 deadline time.Time 代表这个ctx应该被取消的时间。返回的 ok 如果是 false 表示这个context没有设置deadline。连续调用 Deadline 会返回相同的结果。 // Deadline returns the time when work done on behalf of this context // should be canceled. Deadline returns ok==false when no deadline is // set....

July 26, 2024 · 14 min · 2785 words · sirius1y

golang八股文

Golang goroutine内存泄漏 slice导致 获取长字符串中的一段,导致字符串未释放; 获取长slice中的一段导致长slice未释放; 在长切片中新建sllice导致泄漏 channel导致 发送不接受,接收不发送,nil channel 从 channel 里读,但是同时没有写入操作 向 无缓冲 channel 里写,但是同时没有读操作 向已满的 有缓冲 channel 里写,但是同时没有读操作 select操作在所有case上都阻塞() goroutine进入死循环,一直结束不了 向 nil channel 发送和接收数据都将会导致阻塞。这种情况可能在我们定义 channel 时忘记初始化的时候发生。 可见,很多都是因为channel使用不当造成阻塞,从而导致goroutine也一直阻塞无法退出导致的。 传统同步方式sync.mutex,sync.waitgroup导致 用了mutex加lock之后忘记unlock; 在一开始设置了具体数目的wg.wait(n),但是有没有写够足够数量n的wg.Done(),导致wg.Wait()一直等待下去。(正确方式可以使用wg.Add(1)配合wg.Done使用) Go调度器的GMP 在Go语言中,GPM通常指的是Goroutine、Processor和Machine,这是Go调度器(scheduler)的核心组成部分。下面是对每个部分的详细介绍: Goroutine (G): Goroutine是Go语言中的轻量级线程,由Go运行时管理。它们是并发的基本单位,可以被创建和销毁,而无需操作系统级别的线程开销。Goroutine的创建和销毁非常快速,因此可以轻松地创建成千上万个Goroutine。 Goroutine的调度是协作式的,这意味着一个Goroutine在执行时会自愿放弃CPU,让其他Goroutine有机会执行。这种协作式调度使得Go语言能够高效地利用多核处理器。 Processor (P): Processor是Go调度器中的一个抽象概念,代表一个逻辑处理器。每个P都有一个本地运行队列,用于存储待执行的Goroutine。P的数量可以通过环境变量或运行时设置来调整,通常设置为CPU的核心数。 P的主要作用是管理Goroutine的执行。当一个Goroutine被调度到P上时,P会将其分配给一个可用的Machine(M)来执行。 Machine (M): Machine代表一个操作系统线程。M与P关联,负责执行Goroutine。一个M可以与多个P关联,但在任何给定时间,一个M只能执行一个P的Goroutine。 M的主要作用是执行Goroutine的代码。当一个Goroutine被调度到M上时,M会执行该Goroutine的代码,直到该Goroutine自愿放弃CPU或被抢占。 Go调度器的工作原理是将Goroutine(G)分配到Processor(P)上,然后由Machine(M)执行。这种设计使得Go语言能够高效地利用多核处理器,并实现高并发。 在 Go 语言的运行时系统中,Goroutine(简称 G)有多种状态,用于描述它在不同时间点的执行情况。这些状态在 Go 的调度器(GMP 模型)中扮演重要角色。GMP 模型由 Goroutine(G)、工作线程(M)和处理器(P)三部分组成。以下是 G 的主要状态及其转变过程,以及它们与 GMP 模型的关系。 G 的状态 _Gidle:空闲状态。Goroutine 尚未被使用或已经完成执行,等待被分配新任务。 _Grunnable:可运行状态。Goroutine 已经准备好运行,等待被调度器选中运行。 _Grunning:运行状态。Goroutine 正在运行中。 _Gsyscall:系统调用状态。Goroutine 正在执行系统调用,处于阻塞状态,不会被调度器调度。 _Gwaiting:等待状态。Goroutine 在等待某个条件(例如通道操作、定时器、网络 I/O 等)完成。 _Gdead:死亡状态。Goroutine 已经完成执行,无法再被重新使用。 _Gcopystack:堆栈复制状态。Goroutine 的堆栈正在被复制,以调整其大小。 状态转变及其与 GMP 的关系 创建 Goroutine _Gidle -> _Grunnable 创建一个新的 Goroutine,并将其状态设置为 _Grunnable,表示该 Goroutine 准备好运行。 由 P 将新的 Goroutine 添加到其本地运行队列或全局运行队列中。 g := newGoroutine() g....

June 12, 2024 · 3 min · 498 words · sirius1y

【go的源码阅读】channel的实现:chan.go

channel的简单使用 在Go语言中,通道(channel)是一种用于在goroutine之间进行通信和同步的机制。下面是一些简单的通道使用示例,以及它们对应的底层函数调用。 package main import ( "fmt" "time" ) func main() { ch := make(chan int) // 创建通道 go func() { ch <- 42 // 发送数据到通道 }() go func() { value := <-ch // 从通道接收数据 fmt.Println("Received:", value) }() time.Sleep(1 * time.Second) // 等待goroutine完成 close(ch) // 关闭通道 } 底层函数调用 创建通道: ch := make(chan int) 底层调用: makechan(elemtype, size) 发送数据到通道: ch <- 42 底层调用: chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool 从通道接收数据:...

May 10, 2024 · 20 min · 4092 words · sirius1y

点击排行榜scoreboard

项目概述 项目地址:https://github.com/sirius2alpha/scoreboard 使用Redis在服务器上对用户的点击数排序,并返回点击次数排行榜。 技术栈 整体设计 用户界面 排行榜展示区: 显示当前排行榜的状态。 点击按钮: 用户点击来增加他们的计分。 昵称输入和提交: 允许新用户输入昵称并参与排行榜。 实时更新监听: 不需要用户交互,自动更新排行榜。 WebSocket客户端逻辑 建立连接: 当用户访问网站时,建立WebSocket连接。 发送点击事件: 当用户点击按钮时,发送消息到服务器。 接收排行榜更新: 监听来自服务器的排行榜更新,并更新界面。 用户注册: 发送新用户的昵称到服务器。 处理断开连接: 如果用户20秒未操作,发送断开消息到服务器。 后端设计(Gin + Redis) WebSocket服务器 处理WebSocket连接: 接受和管理WebSocket连接。 接收消息: 解析从客户端接收到的消息(点击事件,新用户注册)。 Redis交互: 更新用户的分数并重新排序排行榜。 广播排行榜更新: 将更新后的排行榜发送给所有连接的客户端。 处理断开: 移除30秒未操作的用户。 Redis逻辑 用户分数管理: 存储和更新用户分数。 排行榜排序: 实时更新排行榜。 数据持久化: 保证数据在服务重启后仍然可用。 API设计 本项目API设计采用的是websocket实现。 由于考虑到用户在点击比较频繁,如果采用HTTP会造成头部开销较大,而websocket的头部开销会相对小一些。 消息类型 UserClick: { type: “UserClick”, nickname: “用户昵称” } NewUser: { type: “NewUser”, nickname: “用户昵称” } UserInactive: { type: “UserInactive”, nickname: “用户昵称” }...

January 21, 2024 · 2 min · 287 words · sirius1y

Go学习笔记

配置 Go 工作区 继续之前,请务必仔细阅读此部分。 Go 在组织项目文件方面与其他编程语言不同。 首先,Go 是在工作区的概念下工作的。 工作区就是应用程序源代码所在的位置。 所有 Go 项目共享同一个工作区。 不过,从版本 1.11 开始,Go 已开始更改此方法。 你尚且不必担心,因为我们将在下一个模块中介绍工作区。 现在,Go 工作区位于 $HOME/go,但如果需要,可以为所有项目设置其他位置。 若要将工作区设置为其他位置,可以使用 $GOPATH 环境变量。 在处理更复杂的项目时,此环境变量有助于避免将来出现问题。 Go 工作区文件夹 每个 Go 工作区都包含三个基本文件夹: bin:包含应用程序中的可执行文件。 src:包括位于工作站中的所有应用程序源代码。 pkg:包含可用库的已编译版本。 编译器可以链接这些库,而无需重新编译它们。 例如,工作站文件夹结构树可能与下面的示例类似: bin/ hello coolapp pkg/ github.com/gorilla/ mux.a src/ github.com/golang/example/ .git/ hello/ hello.go Go实战经验 在命令行中输入’code . ‘会唤起VS code编辑当前目录 源码规范 可执行文件都要包含在package main中 import的包必须都要使用,否则报错不进行编译;vs code中保存文件就会自动调整文件格式,并且删除未使用的import 整个package main中只能有一个func main() 变量的声明和初始化 Go是强类型语言,声明的每个变量都绑定到特定的数据类型,并且只接受与此类型匹配的值。 变量声明的方式有很多,格式和其他语言不太一样 最普通的方式:var 变量名称 变量类型 Go也可以像Python那样自动推断变量的类型,有些时候可以不用加类型名称 最常用的方式(只适用于在函数内,声明并初始化一个新的变量):使用冒号等号 age := 32 注意,在函数体外还是只能用var的方式声明和初始化变量 // 变量声明 // 变量声明了必须要使用,否则编译不通过 var first string var second, third string var age int = 1 var ( fisrtly int = 1 secondly string = 2 thirdly = "123" ) var firstName, secondName, agenumber = "123", "456", 32 // 最常见的声明方式 冒号等于号 := 用于声明并初始化变量,不能用于常量的声明 firstName_, secondName_, age_ := "123", "456", 32 // 常量声明f const HTTPstatusOK = 200 const ( StatusOK = 0 StatusConnectionReset = 1 StatusOtherError = 2 ) 数据类型 基本类型:数字、字符串、布尔值 聚合类型:数组、结构体 引用类型:指针、切片、映射、函数、通道 接口类型:接口 基本类型 在 Go 中,如果你不对变量初始化,所有数据类型都有默认值。...

June 20, 2023 · 5 min · 904 words · sirius1y