0%

golang-ants-tour

Ants

high-performance and low-cost goroutine pool
使用Golang编写的低成本高性能的goroutine池

池子类型

  • Pool
  • PoolWithFunc
  • MultiPool
  • MultiPoolWithFunc

源码偶得自旋锁🔗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Copyright 2019 Andy Pan & Dietoad. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package sync

import (
"runtime"
"sync"
"sync/atomic"
)

type spinLock uint32

const maxBackoff = 16

func (sl *spinLock) Lock() {
backoff := 1
for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
// Leverage the exponential backoff algorithm, see https://en.wikipedia.org/wiki/Exponential_backoff.
for i := 0; i < backoff; i++ {
runtime.Gosched()
}
if backoff < maxBackoff {
backoff <<= 1
}
}
}

func (sl *spinLock) Unlock() {
atomic.StoreUint32((*uint32)(sl), 0)
}

// NewSpinLock instantiates a spin-lock.
func NewSpinLock() sync.Locker {
return new(spinLock)
}

两个关键点

  • 指数避让
1
2
3
if backoff < maxBackoff {
backoff <<= 1 // 当小于最大避让次数,左移一位(即为2^(n+1))
}
  • runtime.Gosched

    runtime.Gosched() 是一个用于让出当前 goroutine 的调度器,以便让其他 goroutine 运行的函数。它不会明确指定让出 CPU 的时间,而是将当前 goroutine 放回到调度队列中,允许 Go 调度器选择运行其他可运行的 goroutine。实际上,它让出的是调度机会,而不是具体的时间片。

    • 让出调度:当前 goroutine 将自己放回到调度队列中,并且可能会立即被重新调度运行,或者在其他 goroutine 运行之后再被调度运行。
    • 不阻塞:runtime.Gosched() 不会阻塞当前 goroutine,而是让出调度之后继续执行。

扩展

time.Sleep 与 runtime.Gosched 的对比

  • time.Sleep:
    • 让出 CPU 时间片并进入睡眠状态,直到指定的时间结束。
    • 睡眠时间可以非常短(如 time.Nanosecond),也可以比较长。
    • 在睡眠期间,该 goroutine 不会被调度运行。
  • runtime.Gosched:
    • 立即让出当前 goroutine 的调度权,但没有指定具体的时间。
    • 当前 goroutine 会被放回调度队列,调度器可以立即或者稍后重新调度该 goroutine。