相关信息

算法实现-golang实现交替打印数字字母

该题的关键在意通过channel进行控制

  • 声明一个数字ch,一个字母ch
  • 分别启动一个go 协程, 一直读取channel,读取到则进行消费,消费后,写入对方的信号到通道中

相关信息

golang基础学习

为什么选择go

  • 优势:
    • GC 自动管理内存
      • 不用开发人员手动管理、开发效率更高
    • goroutine 和 channel 的优势以及go的并发模型更加简单
      • 让并发编程相对简单,运行时调度成本低
      • 非常适合微服务、RPC 和网络服务
    • 占用内存低
    • 编译快、运行启动快
    • 标准库完善,云原生生态也非常成熟
  • 缺点
    • 错误处理比较繁琐
    • gc 有一点的开销

context的使用

  • 职责: 控制请求链路的生命周期
    • 取消信号
    • 超时时间/deadline
    • 少量请求级元数据:request_id、trace_id、日志字段等
  • 不放业务数据的原因
    • 类型不安全
      • context.Value的类型是any,取值需要类型断言
    • 依赖关系不清晰
      • 函数需要什么业务参数,应该体现在函数签名里,放进context后,调用双方的依赖会变为“隐式”
    • 滥用会让context变成全局map
      • context变成隐形参数包,排查问题困难
    • 生命周期不匹配
  • 总结

context 的核心用途是控制请求链路的生命周期,比如取消、超时和传递少量请求级元数据。业务数据应该通过明确的函数参数传递,而不是放到 context.Value 里。否则会带来类型不安全、依赖隐式、可读性差和滥用成全局 Map 的问题。Context 里可以放 trace id、request id 这类跨函数、跨服务的元信息,但不应该放订单对象、用户实体这类核心业务数据

context的使用场景

  • context可以协调多个 groutine 中的代码执行“取消”操作,并且可以存储键值对。
    • 最重要的是它是并发安全的。
  • 可以存储键值对,供上下文(协程间)读取【建议不要使用】
  • 优雅的主动取消协程(Cancel)。主动取消子协程运行,用不到子协程了,回收资源。比如一个http请求,客户端突然断开了,就直接cancel,停止后续的操作;
  • 超时退出协程(Timeout),比如如果三秒之内没有执行结束,直接退出该协程;
  • 截止时间退出协程(Deadline),如果一个业务,2点到4点为业务活动期,4点截止结束任务(协程)

Go 程序启动顺序

js
1. 编译期(build) 2. 生成可执行文件 3. 运行时启动(runtime start) 4. 初始化 package 级别变量 5. 执行 init() 6. 执行 main.main()
  • 备注: 局部变量是在函数执行调用时,才会被触发声明

编译时做了那些事情

js
1. 语法检查 2. 类型检查 3. 依赖分析 4. 变量初始化代码生成(决定怎么初始化、按什么顺序初始化) 5. init 调用链生成 6. main 入口标记

gc 垃圾回收

  • Go 的垃圾回收器采用的是并发三色标记清除 + 混合写屏障
    • GC 开始时会短暂 STW,开启写屏障,
      • 目的:获得一致的 Root 快照,保证标记起点正确
    • 然后进入并发标记阶段,
      • 从 GC Roots 出发,利用三色标记遍历所有可达对象。
      • 标记过程中,由于用户 Goroutine 仍在运行,可能修改对象引用关系,
      • 因此 Go 使用混合写屏障维护三色不变性,防止存活对象漏标。
    • 标记结束后再次进行一次很短的 STW,
      • 目的: 保证标记完整,没有漏标对象,才能安全开始回收
    • 随后并发清扫所有未标记的对象并释放内存
  • 混合写屏障解决了什么问题
    • 在并发标记期间维护三色不变性,防止黑色对象直接引用未标记的白色对象,从而避免存活对象被错误回收
  • 混合写屏障具体做了什么
      1. 被删除的旧引用对象,如果还没标记,要标灰
      1. 新写入的引用对象,如果还没标记,也要标灰

内存逃逸

  • 什么是内存逃逸
    • 编译器的一种内存分配策略,对象从栈分配变成了堆分配。
  • 逃逸一定就不好么
    • 逃逸意味着
      • 多一次堆分配;
      • 增加 GC 压力
    • 如果对象生命周期确实超过了函数调用
      • 逃逸就是正确且必要的
  • 场景
    • 给一个引用类对象中的引用类成员进行赋值
      • 可以理解为访问一个引用对象实际上底层就是通过一个指针来间接的访问了
      • 但如果再访问里面的引用成员就会有第二次间接访问
      • 这样操作这部分对象的话,极大可能会出现逃逸的现象
  • case
    • 返回局部变量的地址
    • 闭包捕获局部变量
    • 引用类型的返回
    • 被接口(interface)引用
    • goroutine 使用局部变量
    • 返回包含底层数据的 slice、map 等,使底层对象需要继续存活
    • 对象过大,不适合放在栈上

内存泄漏

  • 核心原因
    • 长期持有对象引用,导致gc无法回收
  • case:
    • 从一个大的slice中截取一小段
      • 只要这部分小的slice一直在使用,那么这个大的slice就一直无法被gc
      • 通过copy(newSlice, large[0:1])
    • channel阻塞卡死
    • channel没有关闭
    • 全局变量、map、slice一直持有引用,导致对象一直可达
    • map的无限增大
    • slice 持有大数组
    • goroutine 因阻塞在 channel、锁或网络 I/O 上无法退出,造成泄漏
      • goroutine 中的消费一直未写入ch导致额永久阻塞
      • goroutine 中写入的ch,一直无法消费导致的阻塞
    • context 未 cancel
    • 不会因为循环引用导致泄漏
      • 标记-清除(Mark & Sweep)GC
    • time.Ticker 或 time.Timer 使用后没有及时 Stop
    • 缓存(如 map、sync.Map)没有淘汰策略,数据持续增长

如何避免channel被重复关闭

  • 重复关闭会发生什么
    • panic
  • 怎么避免
    • 通过sync.once
    • 遵循“由发送者关闭”原则

grpc是什么

  • 概念
    • gRPC 是一个基于 HTTP/2 和 Protobuf 协议的高性能 RPC 框架,
    • 支持多语言和流式通信,
    • 主要用于微服务之间的高性能调用。
    • 相比 REST,它序列化效率更高、连接复用能力更强,
    • 但可读性较差,
    • 更适合内部服务通信。
  • 优势
    • RPC 底层使用 HTTP/2 ,相比 HTTP/1.1 有几个优势:
      • 多路复用(Multiplexing):
        • 一个 TCP 连接可以同时传输多个请求,
        • 不需要像 HTTP/1.1 那样为并发请求建立多个连接或排队等待。
      • 二进制分帧:
        • 传输效率更高,协议解析开销更小。
      • Header 压缩(HPACK):
        • 减少重复请求头带来的网络开销。
    • 使用 Protocol Buffers,相比 JSON,序列化效率高
      • 数据更小
      • 编解码速度更快
      • 类型明确
    • 通过 .proto 定义接口
      • 可以自动生成多语言客户端和服务端代码
      • 具有强类型约束,开发效率高
    • 原生支持 Unary、服务端流、客户端流和双向流四种 RPC 模式
      • 非常适合微服务和实时通信场景
    • 官方生态完善
2025-11-270

相关信息

内存四驱

代码区:存放代码 全局区:常量+全局变量。最终在进程退出时,由操作系统回收。 堆区:空间充裕,数据存放时间较久。一般由开发者分配,启动Golang的GC由GC清除机制自动回收。 栈区:空间较小,要求数据读写性能高,数据存放时间较短暂。由编译器自动分配和释放,存放函数的参数值、局部变量、返回值等、局部变量等(局部变量如果产生逃逸现象,可能会挂在在堆区)

相关信息

相关信息

  • 首先需要用ctrl + z将任务暂停
  • 然后执行下面的操作
js
bg 1&&disown %1

相关信息

gbk编码踩坑记录

背景

工作中,公司内部方法调用的过程中,提示编码无法识别相关的错误提示

解决

部分代码case

py
params = mcpack.dumps(pack, "v2", "gbk") # 这里的功能是对参数进行gbk的编码处理

问题原因是gbk的编码对于某些字符会无法识别,需要修改为gb18030 就可以解决问题

py
params = mcpack.dumps(pack, "v2", "gbk") # 这里的功能是对参数进行gbk的编码处理

相关信息

广度优先搜索学习

以下代码段摘录自算法图解中,可以很好的理解广度优先算法

python
def search(name): search_queue = deque() # 创建队列用于 BFS search_queue += graph[name] # 将起点的邻居加入队列 searched = [] # 用于记录已检查过的人 while search_queue: # 当队列不为空时循环 person = search_queue.popleft() # 从队列左侧取出下一个人 if person not in searched: # 只检查未检查过的人 if person_is_seller(person): # 如果这个人是芒果卖家 print(person + " is a mango seller!") return True # 找到后返回 True else: search_queue += graph[person] # 将此人的邻居加入队列 searched.append(person) # 标记为已检查 return False # 队列为空仍未找到,返回 False

链表算法相关实现

回文链表判断

  • 什么是回文
    • 从两边往中间看,都是相同的 比如121,1221
  • 具体实现
golang
func isPalindrome(head *ListNode) bool { if head == nil || head.Next == nil { return true } slow, fast := head, head for fast.Next != nil && fast.Next.Next != nil { slow = slow.Next fast = fast.Next.Next } tail := reverseList(slow.Next) p1, p2 := head, tail for p2 != nil { if p1.Val != p2.Val { slow.Next = reverseList(tail) return false } p1 = p1.Next p2 = p2.Next } slow.Next = reverseList(tail) return true }

链表反转

go
func reverseList(head *ListNode) *ListNode { var prev *ListNode curr := head for curr != nil { next := curr.Next curr.Next = prev prev = curr curr = next } return prev }
2025-06-270

相关信息

算法学习

为什么学习算法

  • 算法是解决问题的核心能力
  • 提升思维能力(抽象、建模、逻辑推理)
  • 职场竞争力 & 面试必考
  • 写出高质量、高性能的程序
    • 写爬虫 → 如何快速去重(哈希 + 滑动窗口)
    • 日志分析 → 需要排序、聚合、图结构
    • 服务降级 → 用堆来快速取 topN
    • 实时推荐 → 使用布隆过滤器、Trie、倒排索引等算法结构