Chiannel #
channel 类型 #
chan T // can be used to send and receive values of type T
chan<- float64 // can only be used to send float64s
<-chan int // can only be used to receive ints
The <-
operator associates with the leftmost chan possible:
chan<- chan int // same as chan<- (chan int)
chan<- <-chan int // same as chan<- (<-chan int)
<-chan <-chan int // same as <-chan (<-chan int)
chan (<-chan int)
常见用法 #
// 使用 `make(chan val-type)` 创建一个新的通道。
// 通道类型就是他们需要传递值的类型。
messages := make(chan string)
// 使用 `channel <-` 语法 _发送_ 一个新的值到通道中。
// 这里我们在一个新的协程中发送 `"ping"` 到上面创建的 `messages` 通道中。
go func() { messages <- "ping" }()
// 使用 `<-channel` 语法从通道中 _接收_ 一个值。
// 这里我们会收到在上面发送的 `"ping"` 消息并将其打印出来。
msg := <-messages
fmt.Println(msg)
// 如果 ok 的值是 false,表明 接收到的是 特别制造的 0 值 —— 因为发送通道关闭了并且为空(closed and empty)。
msg, ok := <-messages
done channel #
用于保证流水线上每个阶段 goroutine 的退出
golang.org/x/net/context/ctxhttp 中 Do 方法的实现:
// https://github.com/golang/net/blob/release-branch.go1.7/context/ctxhttp/ctxhttp.go
// Do sends an HTTP request with the provided http.Client and returns
// an HTTP response.
//
// If the client is nil, http.DefaultClient is used.
//
// The provided ctx must be non-nil. If it is canceled or times out,
// ctx.Err() will be returned.
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
if client == nil {
client = http.DefaultClient
}
resp, err := client.Do(req.WithContext(ctx))
// If we got an error, and the context has been canceled,
// the context's error is probably more useful.
if err != nil {
select {
case <-ctx.Done():
err = ctx.Err()
default:
}
}
return resp, err
}
注意点 #
- 无缓冲 chan 的发送和接收是否同步
- channel 一定记得 close
- channel 是通过注册相关 goroutine id 实现消息通知的
关闭 channel #
- 不要从接收端关闭 channel
- 不要关闭有多个并发发送者的 channel
如果 sender 是唯一的 sender 或是 channel 最后一个活跃的 sender, 那么你应该在 sender 的 goroutine 关闭 channel, 从而通知 receiver (s)(接收者们) 已经没有值可以读了。
// _关闭_ 一个通道意味着不能再向这个通道发送值了。
// 该特性可以向通道的接收方传达工作已经完成的信息。
package main
import "fmt"
// 在这个例子中,我们将使用一个 `jobs` 通道,将工作内容,
// 从 `main()` 协程传递到一个工作协程中。
// 当我们没有更多的任务传递给工作协程时,我们将 `close` 这个 `jobs` 通道。
func main() {
jobs := make(chan int, 5)
done := make(chan bool)
// 这是工作协程。使用 `j, more := <- jobs` 循环的从 `jobs` 接收数据。
// 根据接收的第二个值,如果 `jobs` 已经关闭了,
// 并且通道中所有的值都已经接收完毕,那么 `more` 的值将是 `false`。
// 当我们完成所有的任务时,会使用这个特性通过 `done` 通道通知 main 协程。
go func() {
for {
j, more := <-jobs
if more {
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
done <- true
return
}
}
}()
// 使用 `jobs` 发送 3 个任务到工作协程中,然后关闭 `jobs`。
for j := 1; j <= 3; j++ {
jobs <- j
fmt.Println("sent job", j)
}
close(jobs)
fmt.Println("sent all jobs")
// 使用前面学到的[通道同步](channel-synchronization)方法等待任务结束。
<-done
}
参考:
Golang chan 避免死锁 #
Golang 的管道 (chan) 无论是否有缓冲,生产者和消费者不可能做到完全解耦, 一旦管道满了,就相当于生产者直接调用消费者。
如果生产者和消费者使用同一个锁,就是死锁了.
所以,一个重要的原则就是,在写管道之前,把所有可能的锁 unlock 掉.
Related posts:
- 异步编程语言的常见坑
- CVPixelBufferRef 与 CVOpenGLTextureRef: 图像处理中内存与显存的交互
- 集成于 iphp 框架的 PHP 并发模型和工具
- 蛇形遍历数组
- 流式布局的原理和代码实现
叶王 © 2013-2024 版权所有。如果本文档对你有所帮助,可以请作者喝饮料。