for-select循环
最基本的模式:
for {
select {
case condition 1:
// ...
case condition 2:
// ...
case condition n:
// ...
default:
// ...
}
}
每个condition
都有机会被执行一次,内部使用了伪随机的策略。如果case
条件全部阻塞,那么执行default
的条件。
防止goroutine泄露
golang的goroutine无法被GC,只能等待goroutine自动执行结束。因此,如果某些情况下,如果goroutine阻塞了,则只能等待整个程序结束才能回收。虽然goroutine内存消耗少,但是也不能一直申请。
基本的解决思路是传入一个终止chan
,代码如下:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
newRandStream := func(done <-chan interface{}) <-chan int {
randStream := make(chan int)
go func() {
defer fmt.Println("newRandStream closure exited")
defer close(randStream)
for {
select {
case randStream <- rand.Int():
case <-done: // 收到终止标记,理解结束
return
}
}
}()
return randStream
}
done := make(chan interface{})
randStream := newRandStream(done)
for i := 0; i < 3; i++ {
fmt.Printf("%d, %d\n", i, <-randStream)
}
close(done)
time.Sleep(1 * time.Second) // 模拟其它工作
}
基本原则是:如果一个goroutine负责创建goroutine,那么它也可以保证停止创建的goroutine。
or-channel
暂时不太理解,留作以后处理
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var or func(channels ...<-chan interface{}) <-chan interface{}
or = func(channels <-chan interface{}) <-chan interface{} {
switch len(channels) {
case 0:
return nil
case 1:
return channels[0]
}
orDone := make(chan interface{})
go func() {
defer close(orDone)
switch len(channels) {
case 2:
select {
case <-channels[0]:
case <-channels[1]:
}
default:
select {
case <-channels[0]:
case <-channels[1]:
case <-channels[2]:
case <-or(append(channels[3:], orDone)...):
}
}
}()
return orDone
}
sig := func(afert time.Duration) <-chan interface{} {
c := make(chan interface{})
go func() {
defer close(c)
time.Sleep(after)
}()
return c
}
start := time.Now()
<-or(
sig(2*time.Hour),
sig(5*time.Minute),
sig(1*time.Second),
sig(1*time.Hour),
sig(1*time.Minute)
)
fmt.Println("done after %v", time.Since(start))
}
错误处理
一般来说,并发进程应该把它们的错误发送到程序的另一个部分,错误中应该有程序的完整状态信息,以便可以做出更明确的决定。构建goroutine时,应该将错误视为一等公民,如果goroutine可能出现错误,那么错误应该和返回值是一起的,通过相同的通信线传递,就像常规的同步函数一样。