Go-Channel

一、Channel 设计原理

Go 中使用 Channel 即通信的方式共享内存,避免了使用内存共享的方式通信,解决了需要限制同一时间能够读写变量的线程数量去解决线程冲突的问题。

Go 语言虽然也能使用 共享内存+互斥锁 进行通信,但同时也提供了 通信顺序进程(Communicating sequential processes,CSP) 的并发模型。即,Goroutine -> Channel -> Goroutine

二、非缓冲信道 - FIFO 同步

默认情况的 Channel 为非缓冲信道,使用 先入先出(FIFO) 处理收发。

  • 先从 Channel 读取数据的 Goroutine 会先接收到数据;
  • 先向 Channel 发送数据的 Goroutine 会得到先发送数据的权利;
package main

import "fmt"

func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // send sum to c
}

func main() {
	s := []int{7, 2, 8, -9, 4, 0}

	c := make(chan int)
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c // receive from c

	fmt.Println(x, y, x+y)
}

三、缓冲信道 - 异步

  • 不遵循 FIFO,发送端和接受端的数据获取处于异步状态
  • 发送方 会向缓冲区中写入数据,然后唤醒接收方,多个接收方会尝试从缓冲区中读取数据,如果没有读取到就会重新陷入休眠;
  • 接收方 会从缓冲区中读取数据,然后唤醒发送方,发送方会尝试向缓冲区写入数据,如果缓冲区已满就会重新陷入休眠;
  • 若缓冲区满载,则会导致发送方阻塞等待
  • Channel 内无值,导致接收方阻塞等待
package main

import "fmt"

func main() {
  // 指定缓冲区大小
  // 仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。
	ch := make(chan int, 2)
	ch <- 1
	ch <- 2
	fmt.Println(<-ch)
	fmt.Println(<-ch)
}

四、Channel 的 range 和 close

c := make(chan int, 10)
// range
for v,ok := range c {
  fmt.Println(v,ok)
}
close(c)
// 关闭信道,禁止数据流入,但可读。
package main

import (
        "fmt"
)

func fibonacci(n int, c chan int) {
        x, y := 0, 1
        for i := 0; i < n; i++ {
                c <- x
                x, y = y, x+y
        }
        close(c)
}

func main() {
        c := make(chan int, 10)
        go fibonacci(cap(c), c)
        // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
        // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
        // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
        // 会结束,从而在接收第 11 个数据的时候就阻塞了。
        for i := range c {
                fmt.Println(i)
        }
}

五、nil Channels

一个为 nil 的 channel,发送和接受操作都会被永久阻塞。

package main
import (  
    "fmt"
    "time"
)
func main() {  
    var ch chan int
    for i := 0; i < 3; i++ {
        go func(idx int) {
            ch <- (idx + 1) * 2
        }(i)
    }
    //get first result
    fmt.Println("result:",<-ch)
    //do other work
    time.Sleep(2 * time.Second)
}
// fatal error: all goroutines are asleep - deadlock!

参考阅读

Go 语言设计与实现 Channel

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值