golang实现自定义锁

165 阅读3分钟

如果想要实现自定义锁,首先要了解go的锁接口定义,然后实现接口里的方法即可(go语言隐式实现接口),go内置的锁的接口定义代码如下:

// A Locker represents an object that can be locked and unlocked.
type Locker interface {
	Lock()
	Unlock()
}

go的锁一般可以基于go的原子操作cas(atomic.CompareAndSwapUint32)来实现,代码如下:

package goroutine

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)
}

加锁的测试代码如下:

func TestLock(t *testing.T) {
	lock := NewSpinLock()
	j := 0
	wg := sync.WaitGroup{}
	for i := 100; i > 0; i-- {
		wg.Add(1)
		go func() {
			defer wg.Done()
			lock.Lock()
			j++
			fmt.Println("j:", j)
			lock.Unlock()
		}()
	}
	wg.Wait()
}

加锁后输出结果如下:

=== RUN   TestLock
j: 1
j: 2
j: 3
j: 4
j: 5
j: 6
j: 7
j: 8
j: 9
j: 10
j: 11
j: 12
j: 13
j: 14
j: 15
j: 16
j: 17
j: 18
j: 19
j: 20
j: 21
j: 22
j: 23
j: 24
j: 25
j: 26
j: 27
j: 28
j: 29
j: 30
j: 31
j: 32
j: 33
j: 34
j: 35
j: 36
j: 37
j: 38
j: 39
j: 40
j: 41
j: 42
j: 43
j: 44
j: 45
j: 46
j: 47
j: 48
j: 49
j: 50
j: 51
j: 52
j: 53
j: 54
j: 55
j: 56
j: 57
j: 58
j: 59
j: 60
j: 61
j: 62
j: 63
j: 64
j: 65
j: 66
j: 67
j: 68
j: 69
j: 70
j: 71
j: 72
j: 73
j: 74
j: 75
j: 76
j: 77
j: 78
j: 79
j: 80
j: 81
j: 82
j: 83
j: 84
j: 85
j: 86
j: 87
j: 88
j: 89
j: 90
j: 91
j: 92
j: 93
j: 94
j: 95
j: 96
j: 97
j: 98
j: 99
j: 100
--- PASS: TestLock (0.00s)
PASS

未加锁情况下测试代码如下:

func TestLock(t *testing.T) {
	//lock := NewSpinLock()
	j := 0
	wg := sync.WaitGroup{}
	for i := 100; i > 0; i-- {
		wg.Add(1)
		go func() {
			defer wg.Done()
			//lock.Lock()
			j++
			fmt.Println("j:", j)
			//lock.Unlock()
		}()
	}
	wg.Wait()
}

未加锁情况下输出如下:

=== RUN   TestLock
j: 1
j: 3
j: 4
j: 5
j: 6
j: 7
j: 8
j: 9
j: 10
j: 2
j: 11
j: 12
j: 14
j: 15
j: 16
j: 17
j: 18
j: 19
j: 21
j: 22
j: 23
j: 24
j: 25
j: 26
j: 27
j: 28
j: 29
j: 30
j: 31
j: 13
j: 33
j: 34
j: 35
j: 36
j: 37
j: 38
j: 39
j: 40
j: 41
j: 32
j: 44
j: 46
j: 49
j: 51
j: 52
j: 54
j: 57
j: 59
j: 62
j: 50
j: 64
j: 75
j: 47
j: 76
j: 56
j: 55
j: 58
j: 42
j: 78
j: 79
j: 81
j: 84
j: 85
j: 65
j: 66
j: 67
j: 68
j: 69
j: 70
j: 71
j: 72
j: 73
j: 43
j: 53
j: 94
j: 96
j: 61
j: 80
j: 20
j: 82
j: 83
j: 63
j: 45
j: 86
j: 87
j: 88
j: 89
j: 48
j: 90
j: 92
j: 93
j: 93
j: 77
j: 95
j: 97
j: 97
j: 60
j: 91
j: 74
j: 98
--- PASS: TestLock (0.00s)
PASS

通过上述两种情况的对比可以发现go的锁的实现还是很成功的。