go 并发顺序一致性

我们都知道go语言起一个线程是很简单的,但这其中也有很多问题(坑)。我在刚了解到并发时,常会思考以这样的方式达到两个线程之间的同步:

var a string
var done bool

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

func main() {
	go setUp()
	for !done {}
	fmt.Println(a)
}

一个线程a更改全局变量,另一个线程b轮询该变量,变量发生变化时,线程b执行某种操作......事实上这种操作是非常危险的。

在Go语言中,同一个Goroutine线程内部,顺序一致性内存模型是得到保证的。但是不同的Goroutine之间,并不满足顺序一致性内存模型,需要通过明确定义的同步事件来作为同步的参考。如果两个事件不可排序,那么就说这两个事件是并发的。为了最大化并行,Go语言的编译器和处理器在不影响上述规定的前提下可能会对执行语句重新排序(CPU也会对一些指令进行乱序执行)。
摘自 advanced-go-programming-book

想要不同线程间同步工作,所谓的同步工作就是线程a执行完之后线程b再执行,必须定义明确的同步事件,实现同步可以通过channel或lock:

channel

一个无管道的channel,取出操作在存入操作之前执行,如果取不出值,就会阻塞。因此必须是打印完之后,主线程才能继续执行下去。

// 使用管道完成同步
func seq1() {
	done := make(chan int)
	go func() {
		fmt.Println("hello world")
		done <- 1
	}()
	<- done
}

lock

使用lock也可以达到上述的目的,互斥锁中,如果抢不到锁,线程就会阻塞。这里主线程中先拿到了锁,必须等另一个goroutine释放锁才可以继续主线程。

// 使用互斥锁完成同步
func main() {
	var mu sync.Mutex
	mu.Lock()
	go func() {
		fmt.Println("hello world")
		mu.Unlock()
	}()
	mu.Lock()
}
posted @ 2021-11-20 20:25  moon_orange  阅读(525)  评论(0编辑  收藏  举报