《Go语言实战》笔记(十三) | Go 并发资源竞争

2023-05-26 0 176

有mammalian,就有天然资源市场竞争,假如三个或是数个goroutine在没互相并行的情况下,出访某一共享资源的天然资源,比如说与此同时对该天然资源展开随机存取时,就会处在互相市场竞争的状况,这是mammalian中的天然资源市场竞争。

mammalian这类并不繁杂,但即使有了天然资源市场竞争的难题,就使他们合作开发Dhanbad的mammalian程序变得复杂出来,即使会引发许多莫名的难题。

package

main

import

(

   “fmt”    “runtime”    

“sync”

)

var (    count int32

wg    sync.WaitGroup )

func main() {    wg.Add(2

)

   go

incCount()

   go

incCount()    wg.Wait()    fmt.Println(count) }

func

incCount() {

   defer

wg.Done()

   for i := 0; i < 2; i++ {        value := count        runtime.Gosched()        value++        count = value    } }

这是一个天然资源市场竞争的例子,他们可以多运行几次这个程序,会发现结果可能是2,也可以是3,也可能是4。即使共享资源天然资源count变量没任何并行保护,所以三个goroutine都会对其展开随机存取,会导致对已经计算好的结果覆盖,以至于产生错误结果,这里他们演示一种可能,三个goroutine他们暂时称之为g1和g2。

g1读取到count为0。

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

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

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

不再继续演示下去了,到这里结果已经错了,三个goroutine互相覆盖结果。他们这里的runtime.Gosched()是让当前goroutine暂停的意思,退回执行队列,让其他等待的goroutine运行,目的是让他们演示天然资源市场竞争的结果更明显。注意,这里还会牵涉到CPU难题,多核会并行,那么天然资源市场竞争的效果更明显。

所以他们对于同一个天然资源的随机存取必须是原子化的,也是说,同一时间只能有一个goroutine对共享资源天然资源展开随机存取操作

共享资源天然资源市场竞争的难题,非常繁杂,并且难以察觉,好在Go为他们提供了一个工具帮助他们检查,这个是go build -race命令。他们在当前项目目录下执行这个命令,生成一个可以执行文件,然后再运行这个可执行文件,就可以看到打印出的检测信息。

go build -race

多加了一个-race标志,这样生成的可执行程序就自带了检测天然资源市场竞争的功能,下面他们运行,也是在终端运行。

./hello

我这里示例生成的可执行文件名是hello,所以是这么运行的,这时候,他们看终端输出的检测结果。

➜  hello ./hello       ================== WARNING: DATA RACE Read at0x0000011a5118 by goroutine 7:  main.incCount()      /Users/xxx/code/go/src/flysnow.org/hello/main.go:25 +0x76 Previous write at 0x0000011a5118 by goroutine6:  main.incCount()      /Users/xxx/code/go/src/flysnow.org/hello/main.go:28 +0x9a Goroutine7(running) created at:  main.main()      /Users/xxx/code/go/src/flysnow.org/hello/main.go:17 +0x77 Goroutine 6(finished) created at:  main.main()      /Users/xxx/code/go/src/flysnow.org/hello/main.go:16 +0

x5f ==================

4

Found 1 data race(s)

看,找到一个天然资源市场竞争,连在那一行代码出了问题,都标示出来了。goroutine 7在代码25行读取共享资源天然资源value := count,而这时goroutine 6正在代码28行修改共享资源天然资源count = value,而这三个goroutine都是从main函数启动的,在16、17行,通过go关键字。

既然他们已经知道共享资源天然资源市场竞争的难题,是即使与此同时有三个或是数个goroutine对其展开了随机存取,那么他们只要保证,与此同时只有一个goroutine随机存取不就可以了,现在他们就看下传统解决天然资源市场竞争的办法—对天然资源加锁。

Go词汇提供了atomic包和sync包里的一些函数对共享资源天然资源并行枷锁,他们先看下atomic包。

package

main

import

(

   “fmt”    “runtime”    “sync”    

“sync/atomic”

)

var (    count int32

   wg    sync.WaitGroup )

funcmain() {    wg.Add(2

)

   go

incCount()    

   go

incCount()    wg.Wait()    fmt.Println(count) }

func

incCount() {

   defer

wg.Done()    

   for i := 0; i < 2; i++ {        value := atomic.LoadInt32(&count)        runtime.Gosched()        value++        atomic.StoreInt32(&count,value)    } }

留意这里atomic.LoadInt32和atomic.StoreInt32三个函数,一个读取int32类型变量的值,一个是修改int32类型变量的值,这三个都是原子性的操作,Go已经帮助他们在底层使用加锁机制,保证了共享资源天然资源的并行和安全,所以他们可以得到正确的结果,这时候他们再使用天然资源市场竞争检测工具go build -race检查,也不会提示有难题了。

atomic包里还有许多原子化的函数可以保证mammalian下天然资源并行出访修改的难题,比如说函数atomic.AddInt32可以直接对一个int32类型的变量展开修改,在原值的基础上再增加多少的功能,也是原子性的,这里不再举例,大家自己可以试试。

atomic虽然可以解决天然资源市场竞争难题,但比较都是比较简单的,支持的数据类型也有限,所以Go词汇还提供了一个sync包,这个sync包里提供了一种互斥型的锁,可以让他们自己灵活的控制哪些代码,与此同时只能有一个goroutine出访,被sync互斥锁控制的这段代码范围,被称之为临界区,临界区的代码,同一时间,只能又一个goroutine出访。刚刚那个例子,他们还可以这么改造。

package

main

import

(

   “fmt”    “runtime”    

“sync”

)

var (    count int32

wg    sync.WaitGroup    mutex sync.Mutex )

func main() {    wg.Add(2

)

   go

incCount()    

   go

incCount()    wg.Wait()    fmt.Println(count) }

func

incCount() {

   defer

wg.Done()    

   for i := 0; i < 2; i++ {        mutex.Lock()        value := count        runtime.Gosched()        value++        count = value        mutex.Unlock()    } }

实例中,新声明了一个互斥锁mutex sync.Mutex,这个互斥锁有三个方法,一个是mutex.Lock(),一个是mutex.Unlock(),这三个之间的区域是临界区,临界区的代码是安全的。

示例中他们先调用mutex.Lock()对有市场竞争天然资源的代码加锁,这样当一个goroutine进入这个区域的时候,其他goroutine就进不来了,只能等待,一直到调用mutex.Unlock() 释放这个锁为止。

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

感谢支持。

《Go语言实战》笔记(十三) | Go 并发资源竞争

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务