giftiaのblog

Go合集

· giftia

协程 线程 进程的区别

  • 进程:资源分配的基本单位,比如启动一个程序
  • 线程:cpu 调度的基本单位,内核态
  • 协程:用户态的轻量级线程

make 和 new 的区别

  • new 初始化任意数据类型并返回指针
  • make 初始化 slice/channel/map,返回可用的数据结构本身

slice 底层

  • 指向底层数组,有长度和容量
  • 扩容:cap<256 翻倍,cap>256 逐步增长到约 1.25 倍

map 底层

  • 2^B 个桶,每个桶 8 组 tophash、key、value
  • 哈希冲突解决使用溢出桶
  • 装载因子>6.5 或溢出桶过多触发扩容,分等量扩容和翻倍扩容,方式是渐进式哈希

channel 底层

  • 环形队列,分有缓存和无缓冲,有等待发送和等待接收队列
  • 并发安全

interface 底层

  • iface:带方法的接口
  • eface:空接口

互斥锁的实现

type Mutex struct {
    state int32  // 状态位
    sema  uint32 // 信号量
}
  • 原子操作加减 state
  • sema 用于阻塞/唤醒协程

锁的两种状态

  • 普通:新来的竞争者通常会和队头抢锁,新的正在占用 cpu,所以一般都能抢到
  • 饥饿:新来的也必须在队尾排队
  • 某个等待者超过 1ms 没拿到锁就进入饥饿模式;队头等待时间小于 1ms 就进入普通模式(或队列为空)

sync.map

  • 适用读多写少
  • 空间换时间,读写分离
  • 读:先读 read map,没有就加锁读 dirty map
  • 写:key 已存在就直接写 read map,新 key 就加锁写 dirty map
  • 同步:read map 未命中次数 >= len(dirty) 时,dirty 升级为 read,然后等下一次写入时再 copy 一个 dirty

线程池(非go)

  • 最大线程数
  • 核心线程数:默认懒创建、默认常驻不回收
  • 空闲存活时间:非核心线程空闲超时被回收,回收下限为核心线程数
  • 工作队列
  • 线程工厂
  • 拒绝策略:报错/静默丢弃/调用者线程运行/丢弃队列最老任务

协程池

协程池的核心目的不是“复用资源”,而是“限制不可控的并发量”

  • worker 数量:控制同时执行任务的最大协程数
  • channel 缓冲大小:控制最多允许多少任务排队等待

panic 的捕获

  • 同一协程
  • recover 必须在 defer func 函数内
  1. 不能捕获
func main() {
	defer recover()
	panic("")
}
  1. 能捕获
func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recovered:", r)
        }
    }()
    panic("")
}

defer 时机

func main() {
    defer fmt.Println("A")
    run()
    defer fmt.Println("C")
    fmt.Println("D")
}

func run() {
    defer fmt.Println("B")
}

正确输出:BDCA(defer只在当前函数作用域内)

goroutine channel

三个 goroutine,一只能打印 A,二只能打印 B,三只能打印 C,如此循环

package main

import "fmt"

func main() {
	chA := make(chan struct{})
	chB := make(chan struct{})
	chC := make(chan struct{})

	go func() {
		for {
			<-chA
			fmt.Print("A")
			chB <- struct{}{}
		}
	}()

	go func() {
		for {
			<-chB
			fmt.Print("B")
			chC <- struct{}{}
		}
	}()

	go func() {
		for {
			<-chC
			fmt.Print("C")
			chA <- struct{}{}
		}
	}()

	chA <- struct{}{} // 启动
	select {}         // 阻塞,保持程序运行
}