Go 语言针对并发提供了传统的同步 goroutine 的机制,就是对共享资源加锁。如果需要顺序访问一个整型变量或者一段代码, atomic 和 sync 包的函数提供了很好的解决方案。
1、原子函数
package main
import (
"fmt"
"runtime"
"sync"
"sync/atomic"
)
var (
counter int64
wg sync.WaitGroup
)
func main() {
wg.Add(2)
go incCounter(1)
go incCounter(2)
wg.Wait()
fmt.Printf("final counter:%d \n",counter)
}
func incCounter(id int) {
defer wg.Done()
for i := 0; i < 2; i++ {
// 安全地对 counter 加1
atomic.AddInt64(&counter, 1)
// 当前 goroutine 从线程退出,并放回队列中
runtime.Gosched()
}
}
atomic 包的 AddInt64 函数会同步整型值的加法,方法就是强制同一时刻只能有一个 goroutine 运行并完成加法操作。
另外,atomic 包的 LoadInt64 和 StoreInt64 这两个原子函数提供了一种安全地读和写一个整型值的方式。
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
var(
shutdown int64
wg sync.WaitGroup
)
func main() {
wg.Add(2)
go doWork("A")
go doWork("B")
time.Sleep(1 * time.Second)
fmt.Println("shut down now")
atomic.StoreInt64(&shutdown, 1)
wg.Wait()
}
func doWork(sigin string) {
defer wg.Done()
fmt.Printf("Doing work:%s \n", sigin)
for {1
time.Sleep(250 * time.Millisecond)
if (atomic.LoadInt64(&shutdown) == 1){
fmt.Printf("shuting work:%s \n", sigin)
break
}
}
}
在上述代码中,如果 doWork goroutine 试图在main函数调用 StoreInt64 的同时调用 LoadInt64函数,那么原子函数会将这些调用互相同步,保证这些操作都是安全的,不会进入竞争状态。
2、互斥锁
另一种同步访问共享资源的方式是使用互斥锁。互斥锁用于在代码上创建一个临界区,保证同一时间只有一个 goroutine 可以执行这个临界区代码。
package main
import (
"fmt"
"runtime"
"sync"
)
var(
counter int
wg sync.WaitGroup
// mutx 用来定义一段代码临界区
mutx sync.Mutex
)
func main() {
wg.Add(2)
go incCounter(1)
go incCounter(2)
wg.Wait()
fmt.Printf("final counter:%d \n", counter)
}
func incCounter(id int) {
defer wg.Done()
for count :=0; count < 2; count++ {
// 加锁
mutx.Lock()
{
value := counter
// 当前 goroutine 从线程退出,并放回到队列中
runtime.Gosched()
value++
counter = value
}
// 释放锁
mutx.Unlock()
}
}