一,Channels
声明后初始化一个无缓冲的channel:
var dataStream chan interface{}
dataStream = make(chan interface{})
将 "<-" 放在chan左边, 声明后初始化一个只读channel:
var dataStream <-chan interface{}
dataStream := make(<-chan interface{})
将 "<-" 放在chan右边, 声明后初始化一个只写channel:
var dataStream chan<- interface{}
dataStream := make(chan<- interface{})
channel类型赋值:
var receiveChan <-chan interface{}
var sendChan chan<- interface{}
dataStream := make(chan interface{})
//有效的操作:
receiveChan = dataStream
sendChan = dataStream
从只写channel中读数据:
writeStream := make(chan<- interface{})
<-writeStream
//输出:
invalid operation: <-writeStream (receive from send-only type chan<-inteface{})
向只读channel中发送数据:
readStream := make(<-chan interface{})
readStream <- struct{}{}
//输出:
invalid operation: readStream <- struct {} literal (send to receive-only type <-chan interface {})
close用法:
intStream := make(chan int)
close(intStream)
integer, ok := <- intStream
fmt.Printf("(%v): %v", ok, integer)
//输出:
(false): 0
我们仍然可以从colse的channel中读取值,返回的是类型零值。
close方法常见应用例子:
1),
intStream := make(chan int)
go func() {
defer close(intStream)
for i := 1; i <= 5; i++ {
intStream <- i
}
}()
for integer := range intStream {
fmt.Printf("%v ", integer)
}
//输出:
1 2 3 4 5
发送完所有数据 defer close掉,for range 结束
2),
begin := make(chan interface{})
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
<-begin//阻塞住goroutine
fmt.Printf("%v has begun\n", i)
}(i)
}
fmt.Println("Unblocking goroutines...")
close(begin)//同时解锁所有goroutine
wg.Wait()
读nil通道:
var dataStream chan interface{}
<-dataStream
写nil通道:
var dataStream chan interface{}
dataStream <- struct{}{}
都会死锁:
fatal error: all goroutines are asleep - deadlock!
注意⚠:
close 值为nil的channel会panic:
var dataStream chan interface{}
close(dataStream)
panic: close of nil channel
channel上面常用操作及相应结果:
如上表:我们有三种操作可以导致goroutine阻塞,三种操作会导致程序panic!
二,Select
select 可以安全的将channels与诸如取消、超时、等待和默认值之类的概念结合在一起。
select看起来就像switch 包含了很多case,然而与switch不同的是:select块中的case语句没有顺序地进行测试,如果没有满足任何条件,执行不会自动失败,如果同时满足多个条件随机执行一个case。
示例:
c1 := make(chan interface{})
close(c1)
c2 := make(chan interface{})
close(c2)
var c1Count, c2Count int
for i := 1000; i >= 0; i-- {
select {
case <-c1:
c1Count++
case <-c2:
c2Count++
}
}
fmt.Printf("c1Count: %d\nc2Count: %d\n", c1Count, c2Count)
//输出:
c1Count: 466
c2Count: 535
select为什么会随机?
go runtime无法了解select语句的意图,也就是说不能推断出你的问题空间或者为什么你将一组channels 放进一个select语句,所以运行时所能做的最好的事情就是在平均情况下运行良好。通过加权平均每个channel被使用的机会。
三,GOMAXPROCS
这是runtime里面的一个函数,名字有误导性,大家通常认为这个函数与机器的逻辑CPU有关,但实际上这个函数控制的OS线程的数量将承载所谓的“工作队列”,在1.5之前,GOMAXPROCS总是被设置为1.
runtime.GOMAXPROCS(runtime.NumCPU())
通常地开发人员需要利用机器上所有内核,所以在随后的版本中它自动的设置成逻辑CPU的数量。
为什么要调整这个值?
例如,我在一个项目上工作,这个项目有一个测试套件,它被race 条件所困扰。不管怎么说,这个团队有几个测试包,有些测试失败了。我们运行测试的基础结构只有四个逻辑cpu,因此在任何一个点上,我们都有四个goroutines同时执行。通过增加GOMAXPROCS超过我们拥有的逻辑cpu数量,我们能够更频繁地触发竞争条件,从而更快地纠正它们。