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代码,会使得当前的退出,但是由于当前的线程持有线程锁,所以调度器只能让这个线程再次继续运行

发表评论

邮箱地址不会被公开。 必填项已用*标注