版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013862108/article/details/88608048
channel:
虽然 他们可以用来同步内存访问; 但他们最好用于在 goroutine 之间传递信息。 命名: 像河流一样,一个channel充当着信息传送的管道,值可以沿着channel传递,然后在下游读出。由于这个特点,通常使用"stream" 来做 chan 变量名的后缀。
创建channel 语句: var dataStream chan interface{} dataStream = make(chan interface{}) 要声明 单向channel 声明 实例化一个只能读取的channel 如下: var dataStream <-chan interface{} dataStream := make(<-chan interface{}) 声明 实例化 一个只能写入 的channel 如下: var dataStream chan<- interface{} dataStream := make(chan<- interface{})
注意: 通常不会看到单向 channel 实例化, 但是会经常看到它们用作函数参数和 返回类型,这是非常有用的,因为当需要时,Go语言会隐式地将双向 channel 转换为 单向 channel。 这里有一个例子: var receiveChan <- chan interface{} var sendChan chan<- interface{} //通常做法 receiveChan = dataStream sendChan = dataStream
func main() {
stringStream := make(chan string)
go func() {
if 0 != 1{
return
}
stringStream <- "Hello channels!"
}()
fmt.Println(<- stringStream)
}
//fatal error: all goroutines are asleep - deadlock!
从channel 中读取数据可以有两个返回值; salutation, ok := <-stringStream
可以使用 range 来遍历 channel;
可以从已关闭的channel 读取数据:
可以从已关闭的channel 读取数据:
func main() {
intStream := make(chan int)
close(intStream)
integer, ok := <- intStream
fmt.Printf("(%v): %v", ok, integer) //(false): 0
}
为什么 已经close channel 了,还可以继续在这个channel上执行读取操作?
这是为了支持有单个上游写入,多个下游读取。
为什么 已经close channel 了,还可以继续在这个channel上执行读取操作? 这是为了支持有单个上游写入,多个下游读取。
关闭 channel 是一种同时给多个 goroutine 发信号的方法。(sync.cond也能 给多个goroutine 发信号) 由于一个被关闭的channel 可以被无数次读取;
func main() {
begin := make(chan interface{})
var wg sync.WaitGroup
for i:=0 ; i<5; i++{
wg.Add(1)
go func(i int) {
defer wg.Done()
<- begin
fmt.Printf("%v has begun\n", i)
}(i)
}
fmt.Println("Unblocking goroutines...")
close(begin)
wg.Wait()
}
//Unblocking goroutines...
//1 has begun
//3 has begun
//2 has begun
//4 has begun
//0 has begun
buffered channel var dataStream chan interface{} dataStream = make(chan interface{}, 4) 创建一个有4个容量的缓冲channel。 这意味着我们可以把4个东西放到channel上,不管它是否被读取; 声明 和实例化 还是分成两行比较好; 通过这种方式缓冲channel 是内存中的FIFO队列,用于并发进程进行通信。
未初始化的channel 默认值 是nil channel 从nil channel中读取数据 会怎样? 死锁/阻塞 <- dataStream //fatal error: all goroutines are asleep - deadlock! 往 nil channel中写入数据 会怎样?死锁/阻塞 dataStream <- struct {}{} //fatal error: all goroutines are asleep - deadlock! 关闭 nil channel 会怎样? 发送 panic 错误
结论: 确保你所使用的channel都会被初始化
表格: 对不同状态的 channel 操作 的结果; todo 表格3-2
如何 组织不同类型的channel来构建健壮和稳定的东西? 正确的配置channel / 分配channel 所有权 把 所有权定义为 实例化, 写入 和关闭 channel 的那个 goroutine。 channel 所有者 对 channel 有一个写访问 chan<- channel 使用者,对channel 有一个只读 <-chan 拥有channel 的 goroutine 应该具备如下: 1。 实例化 channel 2。 执行写操作,或将所有权传递给另一个goroutine 3。 关闭channel 4。 通过一个只读的channel 将它们暴露出来 作为channel的消费者,我只需要担心两件事: 1。 知道 channel 是何时关闭的 2。 正确的处理阻塞
func main(){
chanOwner := func() <-chan int {
resultStream := make(chan int, 5) //5
go func() {
defer close(resultStream) //不加这句话 会报错 因为 range resultStream 不知道何时结束
for i:=0; i<=5; i++{
resultStream <- i
}
}()
return resultStream
}
resultStream := chanOwner()
for result := range resultStream {
fmt.Printf("Received: %d\n", result)
}
fmt.Println("Done receiving!")
}
//将channel 封装在 chan 所有者函数中: 不会出现 往 nil 或 已关闭的 channel 上发生, 而且关闭总是只发生一次, 这从我们的程序中 //消除了大量的风险。原则是 尽量保持channel所有权的范围很小。 /** 这种方式是不好的,如: 一个channel 作为结构体的成员变量,并且有许多方法,它将很快变得不清楚该channel的行为方式