count

 g1读取到count为 0 。

 然后g1暂停了,切换到g2运行,g2读取到count也为 0 。

 g2暂停,切换到g1,g1对count+1,count变为 1 。

 g1暂停,切换到g2,g2刚刚已经获取到值 0 ,对其+1,最后赋值给count还是 1 。

 

 有没有注意到,刚刚g1对count+1的结果被g2给覆盖了,两个goroutine都+1还是 1 。

runtime.Gosched()

所以我们对于同一个资源的读写必须是原子化的,也就是说,同一时间只能有一个goroutine对共享资源进行读写操作。

 

go build -race
-race
hello
value := countcount = value

既然我们已经知道共享资源竞争的问题,是因为同时有两个或者多个goroutine对其进行了读写,那么我们只要保证,同时只有一个goroutine读写不就可以了。现在我们就看下传统解决资源竞争的办法——对资源加锁。

Go语言提供了atomic包和sync包里的一些函数对共享资源同步加锁,我们在此只看下sync:

sync包里提供了一种互斥型的锁,可以让我们自己灵活地控制那些代码,同时只能有一个goroutine访问,被sync互斥锁控制的这段代码范围,被称之为临界区。临界区的代码,同一时间,只能又一个goroutine访问。刚刚那个例子,我们还可以这么改造。

mutex sync.Mutexmutex.Lock()mutex.Unlock()
mutex.Lock()mutex.Unlock()

 这种方式比较灵活,可以让代码编写者任意定义需要保护的代码范围,也就是临界区。除了原子函数和互斥锁,Go还为我们提供了更容易在多个goroutine同步的功能,这就是通道chan。