channel1
package main
import "fmt"
func main() {
ch := make(chan bool, 2)
ch <- true
ch <- true
close(ch)
for i := 0; i < cap(ch) +1 ; i++ {
v, ok := <- ch
fmt.Println(v, ok)
}
}
true true
true true
false false
func main() {
ch := make(chan bool, 2)
ch <- true
ch <- true
close(ch)
for v := range ch {
fmt.Println(v) // 被调用两次
}
}
func main() {
finish := make(chan bool)
var done sync.WaitGroup
done.Add(1)
go func() {
select {
case <-time.After(1 * time.Hour):
case <-finish:
}
done.Done()
}()
t0 := time.Now()
finish <- true // 发送关闭信号
done.Wait() // 等待 goroutine 结束
fmt.Printf("Waited %v for goroutine to stop\n", time.Since(t0))
}
有可能出问题的地方,第一,finish <- true // 发送关闭信号有可能阻塞;第二,如果多个go listen finish,且无法控制具体多少个的时候;前者可以通过缓冲防止阻塞或者select 带default防止阻塞。最好的方式是,利用关闭的channel不会阻塞,切回返回false这一特性,如下,是否需要Wait取决于是否需要等待所有go程返回?
func main() {
const n = 100
finish := make(chan bool)
var done sync.WaitGroup
for i := 0; i < n; i++ {
done.Add(1)
go func() {
select {
case <-time.After(1 * time.Hour):
case <-finish:
}
done.Done()
}()
}
t0 := time.Now()
close(finish) // 关闭 finish 使其立即返回
done.Wait() // 等待所有的 goroutine 结束
fmt.Printf("Waited %v for %d goroutines to stop\n", time.Since(t0), n)
}
当 close(finish) 依赖于关闭 channel 的消息机制,而没有数据收发时,将 finish 定义为 type chan struct{} 表示 channel 没有任何数据;只对其关闭的特性感兴趣。
func main() {
finish := make(chan struct{})
var done sync.WaitGroup
done.Add(1)
go func() {
select {
case <-time.After(1 * time.Hour):
case <-finish:
}
done.Done()
}()
t0 := time.Now()
close(finish)
done.Wait()
fmt.Printf("Waited %v for goroutine to stop\n", time.Since(t0))
}
func main() {
var ch chan bool
ch <- true // 永远阻塞
}
func main() {
var ch chan bool
go func() {
fmt.Println("1")
ch <- true // 永远阻塞
fmt.Println("2")
}()
fmt.Println("sleep start")
time.Sleep(3 * time.Second)
fmt.Println("sleep finish")
ch = make(chan bool, 1) // 也不生效,因为go程传递的ch是没初始化的ch
time.Sleep(10 * time.Second)
}
func main() {
var ch chan bool
<- ch // 永远阻塞
}
// WaitMany 等待 a 和 b 关闭。
func WaitMany(a, b chan bool) {
var aclosed, bclosed bool
for !aclosed || !bclosed {
select {
case <-a:
fmt.Printf("close b\n")
aclosed = true
case <-b:
fmt.Printf("close b\n")
bclosed = true
}
}
}
WaitMany() 用于等待 channel a 和 b 关闭是个不错的方法,但是有一个问题。
假设 channel a 首先被关闭,然后它会立刻返回。
但是由于 bclosed 仍然是 false,程序会进入死循环,
而让 channel b 永远不会被判定为关闭。(实际测试并没有死循环,而是仍有可能跑到b,只不过依靠概率,而改进则每个case只走1次,更为优雅)
func WaitMany(a, b chan bool) {
for a != nil || b != nil {
select {
case <-a:
fmt.Printf("close a\n")
a = nil
case <-b:
fmt.Printf("close b\n")
b = nil
}
}
}
func main() {
a, b := make(chan bool), make(chan bool)
t0 := time.Now()
go func() {
close(a)
close(b)
}()
WaitMany(a, b)
fmt.Printf("waited %v for WaitMany\n", time.Since(t0))
}
func producer(c chan int64, max int) {
defer close(c)
for i:= 0; i < max; i ++ {
c <- time.Now().Unix()
}
}
func consumer(c chan int64) {
var v int64
ok := true
for ok {
if v, ok = <-c; ok {
fmt.Println(v)
}
}
}
type AutoInc struct {
start, step int
queue chan int
running bool
}
func New(start, step int) (ai *AutoInc) {
ai = &AutoInc{
start: start,
step: step,
running: true,
queue: make(chan int, 4),
}
go ai.process()
return
}
func (ai *AutoInc) process() {
defer func() {recover()}()
for i := ai.start; ai.running ; i=i+ai.step {
ai.queue <- i
}
}
func (ai *AutoInc) Id() int {
return <-ai.queue
}
func (ai *AutoInc) Close() {
ai.running = false
close(ai.queue)
}
var sem = make(chan int, MaxOutstanding)
func handle(r *Request) {
sem <- 1 // 等待放行;
process(r) // 可能需要一个很长的处理过程;
<-sem // 完成,放行另一个过程。
}
func Serve(queue chan *Request) {
for {
req := <-queue
go handle(req) // 无需等待 handle 完成。
}
}
func producer(c chan int64, max int) {
defer close(c)
for i:= 0; i < max; i ++ {
select { // randomized select
case c <- 0:
case c <- 1:
}
}
}
c := make(chan int64, 5)
defer close(c)
timeout := make(chan bool)
defer close(timeout)
go func() {
time.Sleep(time.Second) // 等一秒
timeout <- true // 向超时队列中放入标志
}()
select {
case <-timeout: // 超时
fmt.Println("timeout...")
case <-c: // 收到数据
fmt.Println("Read a date.")
}
channel trouble
你可以说,这些模式都不常见。不过……我在我的项目中不得不实现它们中的大多数。每!一!次!可能我不怎么走运,而你的项目会跟写给初学者的指南一样简单。
我知道,你们中的大多数会说“世界是艰辛的,编程是苦难的”。我会继续打击你:至少有一些语言展示了部分解决这些问题的示例。至少,在尝试解决它。Haskell 和 Scala 的类型系统提供了构建强大的高级抽象的能力,甚至自定义控制流来处理并发。而另一阵营的 Clojure 利用动态类型鼓励和共享高级的抽象。Rust 有 channel 和泛型。
让它工作 -> 让它优雅 -> 让它可重用。
现在,第一步已经完成。接下来呢?不要误会,go 是一个有远见的语言:channel 和 goroutine 比起例如 pthread 来说更好,不过是不是真得就停留在此?
func merge(cs ...<-chan int) <-chan int {
var wg sync.WaitGroup
out := make(chan int)
// 为 cs 中每个输入的 channel 启动一个输出用的 goroutine。
// 从 c 中复制值出来直到 c 被关闭,然后又调用 wg.Done。
output := func(c <-chan int) {
for n := range c {
out <- n
}
wg.Done()
}
wg.Add(len(cs))
for _, c := range cs {
go output(c)
}
// 一旦所有输出的 goroutine 完成的,就启动一个 goroutine 来关闭 out。
// 这必须在 wg.Add 调用后启动。
go func() {
wg.Wait()
close(out)
}()
return out
}