前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go 自旋锁

go 自旋锁

原创
作者头像
Johns
发布2022-07-12 13:25:30
9120
发布2022-07-12 13:25:30
举报
文章被收录于专栏:代码工具

定义

CAS算法(compare and swap)

CAS算法是一种有名的无锁算法, 在不使用锁的情况下实现多线程之间的变量同步,而且没有堵塞线程.

CAS基本步骤如下: 需要读写的内存值V==>进行比较的值A==>拟写入的新值B

当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值;

否则不会执行任何操作(比较和替换是一个原子操作)。

自旋锁

自旋锁是指当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断地判断是否能够被成功获取,直到获取到锁才会退出循环。通过死循环检测锁的标志位, 避免了上下文切换的开销, 但是自旋会消耗CPU资源。

自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下,自旋锁一般用于多核的服务器。

互斥锁

对于线程A和线程B来讲,在同一时刻,只允许一个线程对临界资源进行操作,即当A加锁进入临界区对资源操作时,B就必须等待;当A执行完释放锁,退出临界区后,B才能对临界资源进行操作。线程会从 sleep(加锁)—>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销。

所以一般用于临界区持锁时间比较长的操作。

自旋锁实现

基础版本

go官方提供了atomic算法相关的包, 我们可以使用它直接实现一个自旋锁

代码语言:go
复制
package main

import (
	"runtime"
	"sync"
	"sync/atomic"
)
type originSpinLock uint32

func (sl *originSpinLock) Lock() {
	for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
		runtime.Gosched()
	}
}

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

func NewOriginSpinLock() sync.Locker {
	return new(originSpinLock)
}
增加重试指数退避算法
代码语言:txt
复制
package main

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

type spinLock uint32

const maxBackoff = 16

// Lock 加锁
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
		}
	}
}

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

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

性能测试

代码语言:txt
复制
package main

import "testing"

func BenchmarkOriginSpinLock(b *testing.B) {
	spin := NewOriginSpinLock()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			spin.Lock()
			//nolint:staticcheck
			spin.Unlock()
		}
	})
}

func BenchmarkSpinLock(b *testing.B) {
	spin := NewSpinLock()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			spin.Lock()
			//nolint:staticcheck
			spin.Unlock()
		}
	})
}

测试结果

代码语言:txt
复制
goos: darwin
goarch: amd64
pkg: github.com/guirongguo/ghz_demo
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkOriginSpinLock
BenchmarkOriginSpinLock-12    	47265601	        21.54 ns/op
BenchmarkSpinLock
BenchmarkSpinLock-12          	66324406	        18.29 ns/op

参考

go ants源码

自旋锁

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 定义
    • CAS算法(compare and swap)
      • 自旋锁
        • 互斥锁
        • 自旋锁实现
          • 基础版本
            • 增加重试指数退避算法
            • 性能测试
            • 参考
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档