通过加锁实现同步 | Go主题月

1,812 阅读1分钟

Locks

sync包实现两种锁数据类型,sync.Mutex和sync.RWMutex.

var l sync.Mutex
var a string

func f() {
	a = "hello, world"
	l.Unlock()
}

func main() {
	l.Lock()
	go f()
	l.Lock()
	print(a)
}

l.Lock()第一次加锁,然后执行执行函数,等变量a赋值完成后将全局锁释放。l.Lock()能够加到锁的时候,表示f函数已经执行完成。那问题来了,打印完a.Lock,程序运行结束,锁释放需要手动吗?自动释放比手动释放更合理吗?其实本质是一样的,只是手动会多执行一次。

Once

当使用Once.Do(f)时,只有一个线程可以执行这个f函数,只有当f函数执行完成,其他线程才有机会执行f函数。代码示例如下:

var a string
var once sync.Once

func setup() {
	a = "hello, world"
}

func doprint() {
	once.Do(setup)
	print(a)
}

func twoprint() {
	go doprint()
	go doprint()
}

错误示例

var a, b int

func f() {
	a = 1
	b = 2
}

func g() {
	print(b)
	print(a)
}

func main() {
	go f()
	g()
}

f函数和g函数同时执行,所以输出结果不确定,不能达到预期。结果可能是0,1。

var a string
var done bool

func setup() {
	a = "hello, world"
	done = true
}

func doprint() {
	if !done {
		once.Do(setup)
	}
	print(a)
}

func twoprint() {
	go doprint()
	go doprint()
}

双重锁校验,但是不能保证输出的结果为hello,world,有可能输出为空。

var a string
var done bool

func setup() {
	a = "hello, world"
	done = true
}

func main() {
	go setup()
	for !done {
	}
	print(a)
}

当主函数执行到for !done时,done可能不为true,所以主函数一直阻塞在这个循环体。

type T struct {
	msg string
}

var g *T

func setup() {
	t := new(T)
	t.msg = "hello, world"
	g = t
}

func main() {
	go setup()
	for g == nil {
	}
	print(g.msg)
}

即使g可能不为nil,但是g.msg可能为空。