select 基本使用
func generator() chan int {
out := make(chan int)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
out <- i
i++
}
}()
return out
}
func main() {
var c1, c2 = generator(), generator()
for {
select {
case n := <-c1:
fmt.Println("received from c1 ", n)
case n := <-c2:
fmt.Println("received from c2 ", n)
//default:
// fmt.Println("No value received")
}
}
}
看下执行结果,实际上 这个select 关键字的作用就是 先从哪个chan 拿到值 就走哪个case 仅此而已。
n个chan 里面 选择一个 最先到达的。
atomic
大部分的语言 都有原子操作和锁的概念。go也不例外
go也一样 提供了原生的 原子操作的 函数。这里就不演示了,和java其实差不多
我们具体看下go语言中的锁 怎么使用
我们来模仿一下这个atomic的实验
package main
import (
"fmt"
"time"
)
type atomicInt int
func (a *atomicInt) increase() {
*a++
}
func (a *atomicInt) get() int{
return int(*a)
}
func main() {
var a atomicInt
a.increase()
go func() {
a.increase()
}()
time.Sleep(time.Millisecond)
fmt.Println(a.get())
}
显然这段程序 看起来是有问题的 对吧。 因为你的increase 和 get函数 这2个函数 没有加锁,看上去是可以并发执行的。 这当然会在高并发的时候 导致最终的结果不正确
但实际上我们跑起来看这段程序的时候 发现结果是2 是符合我们预期的,这是为啥? 这是因为 这个场景 在单个goroutine的情况下 比较难以模仿出 冲突的情况,但是通过一些命令是可以找到证据的。
我们使用go run -race 是可以查一下 程序的运行过程的
可以看出来 已经给出了警告提示 这里是有data race的。
并且告诉你了 15行和25行 一起读了 一起读问题不大 但是下面是一起写 那就有问题了
下面就利用mutex 来加锁
package main
import (
"fmt"
"sync"
"time"
)
type atomicInt struct {
value int
lock sync.Mutex
}
func (a *atomicInt) increase() {
a.lock.Lock()
defer a.lock.Unlock()
a.value++
}
func (a *atomicInt) get() int {
a.lock.Lock()
defer a.lock.Unlock()
return a.value
}
func main() {
var a atomicInt
a.increase()
go func() {
a.increase()
}()
time.Sleep(time.Millisecond)
fmt.Println(a.get())
}
再看一下 就ok了