Go语言提供了传统的共享资源锁住的能力
atomic和sync包中的函数提供了很好的解决,我们看下如下的几个函数
原子函数
操作系统自带的加锁机制
使用原子函数来修正代码清单中的竞争状态
var (
// counter is a variable incremented by all goroutines.
counter int64
// wg is used to wait for the program to finish.
wg sync.WaitGroup
)
// main is the entry point for all Go programs.
func main() {
// Add a count of two, one for each goroutine.
wg.Add(2)
// Create two goroutines.
go incCounter(1)
go incCounter(2)
// Wait for the goroutines to finish.
wg.Wait()
// Display the final value.
fmt.Println(“Final Counter:”, counter)
}
// incCounter increments the package level counter variable.
func incCounter(id int) {
// Schedule the call to Done to tell main we are done.
defer wg.Done()
for count := 0; count < 2; count++ {
// Safely Add One To Counter.
atomic.AddInt64(&counter, 1)
// Yield the thread and be placed back in queue.
runtime.Gosched()
}
}
上述代码中我们利用atomic的AddInt64函数更新的整型值
这样,我们保证同一时刻只能有一个goroutine运行并完成这个加法操作
从而保证了同步的操作
在atomic包下还有两个相关的原子函数LoadInt64和 storeInt64 分别是原子性的读和写一个整型值
然后是互斥锁
同步访问共享资源的方式使用互斥锁,互斥锁在代码上创建一个临界区,保证同一时间只能有一个goroutine来执行这个临界区代码
对于互斥锁的使用,可以看如下的代码
package main
import (
“fmt”
“runtime”
“sync”
)
var (
// counter is a variable incremented by all goroutines.
counter int
// wg is used to wait for the program to finish.
wg sync.WaitGroup
// mutex is used to define a critical section of code.
mutex sync.Mutex
)
// main is the entry point for all Go programs.
func main() {
// Add a count of two, one for each goroutine.
wg.Add(2)
// Create two goroutines.
go incCounter(1)
go incCounter(2)
// Wait for the goroutines to finish.
wg.Wait()
fmt.Printf(“Final Counter: %d\n”, counter)
}
// incCounter increments the package level Counter variable
// using the Mutex to synchronize and provide safe access.
func incCounter(id int) {
// Schedule the call to Done to tell main we are done.
defer wg.Done()
for count := 0; count < 2; count++ {
// Only allow one goroutine through this
// critical section at a time.
mutex.Lock()
{
// Capture the value of counter.
value := counter
// Yield the thread and be placed back in queue.
runtime.Gosched()
// Increment our local value of counter.
value++
// Store the value back into counter.
counter = value
}
mutex.Unlock()
// Release the lock and allow any
// waiting goroutine through.
}
}
我们先是创建sync包中的Mutex,然后利用go函数共享这个Mutex,利用其进行加锁,具体的加锁步骤在函数中体现出来了
mutex.Lock()
mutex.Unlock()
在lock和unlock之间,使用大括号只是为了让临界区更清晰了,但不是必需的,同一时刻只能有一个goroutine可以进入临界区,进入临界区之后,只有调用Unlock之后,其他goroutine才能进入临界区,但是我们使用runtime.Gosched代码,会使得当前的退出,但是由于当前的线程持有线程锁,所以调度器只能让这个线程再次继续运行