We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Go 语言中最常见的、也是经常被人提及的设计模式就是:不要通过共享内存的方式进行通信,应该通过通信的方式共享内存。在很多主流的编程语言中,多个线程传递数据的方式一般都是共享内存,为了解决线程竞争,我们需要通过加互斥锁的方式限制同一时间能够读写这些变量的线程数量。
Thread1 ====> Memory ====> Thread2
然而这与 Go 语言鼓励的设计并不相同。
虽然我们在 Go 语言中也能使用共享内存加互斥锁进行通信,但是 Go 语言提供了一种不同的并发模型,即通信顺序进程(Communicating sequential processes,CSP)。goroutine 和 Channel 分别对应 CSP 中的实体和传递信息的媒介,goroutine 之间会通过 Channel 传递数据。
goroutine1 ====> Channel ====> goroutine2
上面两个 goroutine,一个会向 Channel 中发送数据,另一个会从 Channel 中接收数据,它们两者能够独立运行并不存在直接关联,但是能通过 Channel 间接完成通信。
目前的 Channel 收发操作均遵循了先进先出的设计,具体规则如下:
可以往 Channel 中发送数据,也可以从 Channel 中接收数据,所以,Channel 类型分为:只接收、只发送、双向收发 三种类型。
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
相应的语法定义如下:
var chan1 chan int // 可以双向收发int数据的 双向chan var chan2 <-chan int // 只能从通道接收到int数据的 单向chan var chan3 chan <- int //只能向 chan 发送int 数据的 单向chan
未初始化的 chan 的零值是 nil,不能直接使用。通过内置的 make 函数,我们可以初始化一个 chan
make(chan int, 100)
上面我们初始化了一个容量为100的 chan ,我们把这样的 chan 叫做 buffered chan。如果没有设置容量,那么容量默认是 0,我们把这样的 chan 叫做 unbuffered chan。
make(chan int)
对于 buffered chan,如果 chan 的缓存循环列表中还有数据,那么goroutine 从 chan 接收数据的时候就不会阻塞,如果 chan 的容量还未填满,goroutine 给它发送数据时也不会阻塞,否则就会发送阻塞。unbuffered chan 只有读写都准备好之后才不会阻塞。
往通道中发送一个数据使用 ch<-,发送数据是一条语句:
ch <- 2000
这里的 ch 是 chan int 类型或者是 chan <-int 类型。
从通道中接收一条数据使用 <-ch,接收数据也是一条语句:
x := <-ch // 把接收的一条数据赋值给变量x foo(<-ch) // 把接收的一个的数据作为参数传给函数 <-ch // 丢弃接收的一条数据
这里的 ch 类型是 chan T 或者 <-chan T。
从通道中接收数据时,还可以返回两个值。
v, ok := <-ch
第一个值 v 存储从 chan 中读取到的元素,第二个值是 bool 类型,代表是否成功地从 chan 中读取到一个值,如果第二个参数是 false,表明 chan 已经被 close 而且 chan 中没有缓存的数据,这个时候,第一个值是零值。所以,如果从 chan 读取到一个零值,可能是 sender 真正发送的零值,也可能是 chan 已经被 close 并且已经没有缓存元素而产生的零值。
Go 内建的函数 close、cap、len 都可以操作 chan 类型:close 会把 chan 关闭掉,cap 返回 chan 的容量,len 返回 chan 中缓存的还未被取走的元素数量。
send 和 recv 都可以作为 select 语句的 case clause,如下面的例子:
func main() { var ch = make(chan int, 10) for i := 0; i < 10; i++ { select { case ch <- i: case v := <-ch: fmt.Println(v) } } }
chan 还可以应用于 for-range 语句中,比如:
for v := range ch { fmt.Println(v) }
在通道被关闭后,会自动退出for range 循环的执行。
下面的可以用于抽干Channel内缓存的元素
for range ch { } // 或者 for _ := range ch { }
Channel 并不能用于解决所有并发编程中的问题,有些场景适合使用Channel,而有的场景使用并发同步原语更加简单。 具体什么时候用Channel 什么时候用 sync 原语,可以用一下标准衡量:
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Go 语言中最常见的、也是经常被人提及的设计模式就是:不要通过共享内存的方式进行通信,应该通过通信的方式共享内存。在很多主流的编程语言中,多个线程传递数据的方式一般都是共享内存,为了解决线程竞争,我们需要通过加互斥锁的方式限制同一时间能够读写这些变量的线程数量。
然而这与 Go 语言鼓励的设计并不相同。
虽然我们在 Go 语言中也能使用共享内存加互斥锁进行通信,但是 Go 语言提供了一种不同的并发模型,即通信顺序进程(Communicating sequential processes,CSP)。goroutine 和 Channel 分别对应 CSP 中的实体和传递信息的媒介,goroutine 之间会通过 Channel 传递数据。
上面两个 goroutine,一个会向 Channel 中发送数据,另一个会从 Channel 中接收数据,它们两者能够独立运行并不存在直接关联,但是能通过 Channel 间接完成通信。
目前的 Channel 收发操作均遵循了先进先出的设计,具体规则如下:
基本用法
可以往 Channel 中发送数据,也可以从 Channel 中接收数据,所以,Channel 类型分为:只接收、只发送、双向收发 三种类型。
相应的语法定义如下:
类型声明
初始化
未初始化的 chan 的零值是 nil,不能直接使用。通过内置的 make 函数,我们可以初始化一个 chan
上面我们初始化了一个容量为100的 chan ,我们把这样的 chan 叫做 buffered chan。如果没有设置容量,那么容量默认是 0,我们把这样的 chan 叫做 unbuffered chan。
对于 buffered chan,如果 chan 的缓存循环列表中还有数据,那么goroutine 从 chan 接收数据的时候就不会阻塞,如果 chan 的容量还未填满,goroutine 给它发送数据时也不会阻塞,否则就会发送阻塞。unbuffered chan 只有读写都准备好之后才不会阻塞。
发送数据
往通道中发送一个数据使用 ch<-,发送数据是一条语句:
这里的 ch 是 chan int 类型或者是 chan <-int 类型。
接收数据
从通道中接收一条数据使用 <-ch,接收数据也是一条语句:
这里的 ch 类型是 chan T 或者 <-chan T。
从通道中接收数据时,还可以返回两个值。
第一个值 v 存储从 chan 中读取到的元素,第二个值是 bool 类型,代表是否成功地从 chan 中读取到一个值,如果第二个参数是 false,表明 chan 已经被 close 而且 chan 中没有缓存的数据,这个时候,第一个值是零值。所以,如果从 chan 读取到一个零值,可能是 sender 真正发送的零值,也可能是 chan 已经被 close 并且已经没有缓存元素而产生的零值。
其他操作
Go 内建的函数 close、cap、len 都可以操作 chan 类型:close 会把 chan 关闭掉,cap 返回 chan 的容量,len 返回 chan 中缓存的还未被取走的元素数量。
send 和 recv 都可以作为 select 语句的 case clause,如下面的例子:
chan 还可以应用于 for-range 语句中,比如:
在通道被关闭后,会自动退出for range 循环的执行。
下面的可以用于抽干Channel内缓存的元素
Channel和并发同步原语怎么选择
Channel 并不能用于解决所有并发编程中的问题,有些场景适合使用Channel,而有的场景使用并发同步原语更加简单。 具体什么时候用Channel 什么时候用 sync 原语,可以用一下标准衡量:
The text was updated successfully, but these errors were encountered: