Chiannel

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:

  1. 异步编程语言的常见坑
  2. CVPixelBufferRef 与 CVOpenGLTextureRef: 图像处理中内存与显存的交互
  3. 集成于 iphp 框架的 PHP 并发模型和工具
  4. 蛇形遍历数组
  5. 流式布局的原理和代码实现

参考 #


本文访问量

本站总访问量

本站总访客数