Gox语言中,除了可以直接使用Go语言中的通道对象(chan)之外,也直接引入了Go语言标准库中的sync包,因此可以直接使用共享锁Mutex对象或者读写锁RWMutex对象来处理并发操作中共享数据安全的问题。同时,也可以使用sync包中的WaitGroup对象来实现几个并发线程间的同步。
我们来看下面的例子:
valueG = 0
groupG = new(sync.WaitGroup)
mutexG = new(sync.Mutex)
addRoutine = fn(countA) {
for i = 0; i < countA; i++ {
mutexG.Lock()
valueG = valueG + 2
mutexG.Unlock()
}
groupG.Done()
}
minusRoutine = fn(countA) {
for i = 0; i < countA; i++ {
mutexG.Lock()
valueG = valueG - 2
mutexG.Unlock()
}
groupG.Done()
}
times = 5000
groupG.Add(2)
go addRoutine(times)
go minusRoutine(times - 2)
groupG.Wait()
printfln("valueG: %v", valueG)
这段代码中,addRoutine和minusRoutine是两个线程函数,分别用于将一个共享的全局变量valueG每次加2或者减2,为了解决共享冲突问题,定义了一个共享锁变量mutexG,这样就可以用常规的Lock(加锁)和Unlock(解锁)操作了。
另外,为了在主进程中等待两个线程都执行完毕后再继续,使用了一个WaitGroup变量groupG,一开始用groupG.Add(2)这一句设置了计数器为2表示要等待两个线程退出,之后每一次线程中调用groupG.Done()表示退出了一个线程,计数器为0时,groupG.Wait()函数才会结束阻塞继续执行后面的语句,这样就实现了多个线程的同步(这种情况也叫线程的归并)。
最后,这段代码无论执行多少次(本例中加法线程执行5000次,由times变量确定,减法线程少执行2次),都会输出以下的结果:
λ gox sync.gox
valueG: 4
说明是解决了共享冲突的,因为如果没有处理好共享冲突,结果不一定会恰好为4(应当为4是因为减法的线程少执行了两次)。
* 注:由于0.988版本后,为了减少不必要的文件体积,Gox已经放弃了其他脚本引擎,仅支持Qlang引擎,因此本文中的内容已经无效,仅留作对旧版本的参考。
下面是Anko引擎版的代码与执行结果:
sync = import("sync")
tk = import("tk")
var valueG = 0
var groupG = make(sync.WaitGroup)
var mutexG = make(sync.Mutex)
func addRoutine(countA) {
for i = 0; i < countA; i++ {
mutexG.Lock()
valueG = valueG + 2
mutexG.Unlock()
}
groupG.Done()
}
func minusRoutine(countA) {
for i = 0; i < countA; i++ {
mutexG.Lock()
valueG = valueG - 2
mutexG.Unlock()
}
groupG.Done()
}
times = 5000
groupG.Add(2)
go addRoutine(times)
go minusRoutine(times - 2)
groupG.Wait()
printfln("valueG: %v", valueG)
这段代码无论执行多少次,都会输出以下的结果:
λ gox scripts\sync.ank
valueG: 4