Go Concurrency Patterns

golang的一些并发编程中的模式。

Buffered Channel as Semaphore

一个effective go上面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var sem = make(chan int, MaxOutstanding)

func init() {
for i := 0; i < MaxOutstanding; i++ {
sem <- 1
}
}

func Serve(queue chan *Request) {
for req := range queue {
<-sem
go func(req *Request){
process(req)
sem <- 1
}(req)
}
}

MaxOutstanding限制了调用process的并发数。注意这里把req作为函数的参数传入这个closure,保证了closure里面的req是一个新的副本,因为range那里的req变量每次都是复用的。

当然上面这个例子还可以这么实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func handle(queue chan *Request) {
for r := range queue {
process(r)
}
}

func Serve(clientRequests chan *Request, quit chan bool) {
// Start handlers
for i := 0; i < MaxOutstanding; i++ {
go handle(clientRequests)
}
<-quit // Wait to be told to exit.
}


## Timeout

timeout := make(chan bool, 1)
go func() {
time.Sleep(1 * time.Second)
timeout <- true
}()

select {
case <-ch:
// a read from ch has occurred
case <-timeout:
// the read from ch has timed out
}

timeout channel的buffer是1,保证了timeout goroutine向channel发完数据后就退出,而不必block住一直到select那里从timeout读出数据。

golang中提供了time.After来完成上面timeout goroutine的工作。

1
2
3
4
5
6
select {
case <-ch:
// a read from ch has occurred
case <-time.After(1 * time.Second):
// the read from ch has timed out
}