添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

请求⽣命周期开始时,pool.Get,请求结束时,pool.Put。在 fasthttp 中有⼤量应⽤

https://github.com/valyala/fasthttp/blob/b433ecfcbda586cd6afb80f41ae45082959dfa91/server.go#L402

sync.Pool 发⽣ GC 时:

semaphore

是锁的实现基础,所有同步原语的基础设施。

sync.Mutex

sync.RWMutex

sync.Map

https://www.figma.com/proto/FMzUIdkjm4BEHSpwWFecew/concurrency?page-id=6%3A15&node-
id=6%3A16&viewport=-46%2C368%2C0.5078045725822449&scaling=min-zoom

sync.Waitgroup

Counter 减到 0 时,要唤醒所有 sema 上阻塞的 sudog

并发编程模式举例

CSP 和传统并发模式

Fan-in,合并多个 channel 操作

Or channel

任意 channel 返回
全部返回

Pipeline

串联在⼀起的 channel

并发同时保序

常⻅的并发 bug

死锁

RWR 死锁

循环等待死锁

注意: 死锁问题需要通过 pprof 进⼊ goroutine ⻚⾯查看

Map concurrent writes/reads

崩溃时输出 stderr,请注意重定向你的 stderr 到单独的⽂件中

Channel 关闭 panic

Channel closing principle

  • M receivers, one sender, the sender says “no more sends” by closing the data channel
  • One receiver, N senders, the only receiver says “please stop sending more” by closing an additional signal channel
  • M receivers, N senders, any one of them says “let’s end the game” by notifying a moderator to close an additional signal channel
  • go101.channel-closing

    fn() 超时后,ch <- result 阻塞

    goroutine 永久泄露

    wait group 使⽤不当,永久阻塞

    context.WithCancel

    内部启动 goroutine,在 ctx 被覆盖后泄露

    闭包捕获本地变量

    启动goroutine前要保证Add完成

    并发操作 channel 时,多次关闭同⼀个 channel

    Fn 耗时很久,但进⼊之前没有判断外部给的stopCh 中的通知浪费算⼒

    内存模型

    对于应⽤开发的同学来说,只要记住,使⽤ 显式同步 就可以保证正确性。

    现代计算机的多级存储结构
    L1D cache ⼜会被划分为多个cache line,每个 cache line = 64 bytes

    http://15418.courses.cs.cmu.edu/spring2015/lecture/basicarch/slide_042

    L1 cache ⼜被划分为更细粒度的 cacheline,下⾯是在服务器上获取 L1 cache line size 的命令

    1
    2
    $ getconf LEVEL1_DCACHE_LINESIZE
    64

    Runtime 中的 cacheline pad

    http://15418.courses.cs.cmu.edu/spring2015/lecture/basicarch/slide_042

    多核⼼给我们带来的问题:

  • 单变量的并发操作也必须⽤同步⼿段,⽐如 atomic
  • 全局视⻆下观察到的多变量读写的顺序可能会乱序
  • 单变量的原⼦读/写,多核⼼使⽤ mesi 协议保证正确性

    Mesi 协议是以整个 cache line 为单位进⾏的

    https://www.scss.tcd.ie/Jeremy.Jones/VivioJS/caches/MESIHelp.htm

    多核⼼执⾏时,CPU 和编译器可能对读写指令进⾏重排,使⽤ Litmus 测试观察内存重排:

    检查两个核⼼的 EAX 是不是都为 0

    https://github.com/herd/herdtools7

    False sharing:因为 CPU 处理读写是以 cache line 为单位,所以在并发修改变量时,会⼀次性将其它 CPU core 中的cache line invalidate 掉,导致未修改的内存上相邻的变量也需要同步,带来额外的性能负担。

    True sharing:多线程确实在共享并更新同⼀个变量/内存区域。

    Happen-before 到底是什么?

    同⼀个 goroutine 内的逻辑有依赖的语句执⾏,满⾜顺序关系。
    编译器/CPU 可能对同⼀个 goroutine 中的语句执⾏进⾏打乱,以提⾼性能,但不能破坏其应⽤原有的逻辑。
    不同的 goroutine 观察到的共享变量的修改顺序可能不⼀样。

    初始化:
    A pkg import B pkg,那么 B pkg 的 init 函数⼀定在 A pkg 的 init 函数之前执⾏。
    Init 函数⼀定在 main.main 之前执⾏

    Goroutine 创建:
    Goroutine 的创建(creation)⼀定先于 goroutine 的执⾏(execution)

    Goroutine 结束:
    在没有显式同步的情况下,goroutine 的结束没有任何保证,可能被执⾏,也可能不被执⾏

    Channel 收/发:
    A send on a channel happens before the corresponding receive from that channel completes.

    这⾥ c <- 0 ⼀定先于 <- c 执⾏完,所以 print ⼀定能打印出 hello world

    The closing of a channel happens before a receive that returns a zero value because the channel is closed.

    close(c) ⼀定先于 <-c 执⾏完,所以这⾥也可以保证打印出 hello world。

    Channel 收/发:
    A receive from an unbuffered channel happens before the send on that channel completes.

    ⽆ buffer 的 chan receive 先于 send 执⾏完,这⾥也可以保证打印出 hello world

    Lock:For any sync.Mutex or sync.RWMutex variable l and n < m, call n of l.Unlock() happens before call m of l.Lock()returns

    Unlock ⼀定先于 Lock 函数返回前执⾏完

    Once:A single call of f() from once.Do(f) happens (returns) before any call of once.Do(f) returns.

    本质是在⽤户不知道 memory barrier 概念和具体实现的前提下,能够按照官⽅提供的 happen-before 正确进⾏并发编程。

    Memory barrier

    在并发编程中的 memory barrier 和 GC 中的 barrier 不是⼀回事。
    Memory barrier 是为了防⽌各种类型的读写重排:

    ⽽ GC 中的 read/write barrier 则是指堆上指针修改之前插⼊的⼀⼩段代码。

    References

    https://wudaijun.com/2018/02/go-sync-map-implement/
    https://github.com/kat-co/concurrency-in-go-src
    https://speakerdeck.com/kavya719/understanding-channels
    https://www.zenlife.tk/concurrency-with-keep-order.md?hmsr=joyk.com&utm_source=joyk.com&utm_medium=referral
    https://golang.org/ref/mem
    https://www.hardwaretimes.com/difference-between-l1-l2-and-l3-cache-what-is-cpu-cache/
    https://github.com/lotusirous/go-concurrency-patterns
    https://songlh.github.io/paper/go-study.pdf
    https://github.com/cch123/golang-notes/blob/master/memory_barrier.md

  • 内置并发结构:sync.Cond
  • 进阶话题:如 acquire、release、sequential consistency。
  • Lock-Free,Wait-free 等等
  • 扩展并发原语:SingleFlight,ErrGroup 等
  • 本文链接: https://octopuslian.github.io/2021/11/12/go-senior-engineer-6-go-concurrent-programming-practice/ 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!