网站系统使用手册,制作网线的要点,单页设计风格,丝瓜app官网下载安装ioChannel
下面的几个例子将会展示如何定义一个 channel#xff1a;
func chanDemo() {var c chan int // chan int 的含义是, c 是一个 channel, 里面的内容是 int// 上面的声明语句将会创建一个 nil channel, c nil, 它的作用将在 select 当// 中体现
}创建一个非 nil 的 c…Channel
下面的几个例子将会展示如何定义一个 channel
func chanDemo() {var c chan int // chan int 的含义是, c 是一个 channel, 里面的内容是 int// 上面的声明语句将会创建一个 nil channel, c nil, 它的作用将在 select 当// 中体现
}创建一个非 nil 的 channel 的方法是使用内建的 make 函数
package mainimport fmtfunc chanDemo() {c : make(chan int)go func() { // 由于 channel 是 goroutine 之间的通道for { // 因此应该建立一个 goroutine 不断收数据n : -c // 从 channel 收数据fmt.Println(n)}}()c - 1 // 向 channel 发数据c - 2 // 向 channel 发数据
}func main() {chanDemo()
}
Channel 也是 Golang 当中的一等公民
在函数式编程的学习过程中我们已经看到函数是 Golang 的一等公民它可以作为参数也可以作为返回值。而 Channel 同样也是 Golang 的一等公民它同样可以作为参数也可以作为返回值。
为了展示其用法我们对上面的代码进行修改首先将上面代码当中的匿名函数单独定义为一个函数
package mainimport (fmttime
)func worker(c chan int) { // channel 作为参数for {n : -c // 从 channel 收数据fmt.Println(n)}
}func chanDemo() {c : make(chan int)go worker(c)c - 1 // 向 channel 发数据c - 2 // 向 channel 发数据time.Sleep(time.Second)
}func main() {chanDemo()
}
其输出仍然是 1 和 2。
为了进一步体现 channel 是一等公民使用 var 声明一个存储 10 个 chan int 型 channel 的数组并在循环中使用内建的 make 对每个 chan int 进行构造并将 chan int 分发给 10 个 workers
package mainimport (fmttime
)func worker(id int, c chan int) { // channel 作为参数for {n : -c // 从 channel 收数据fmt.Printf(Worker %d received %c\n, id, n)}
}const worker_num int 10func chanDemo() {var channels [10]chan int // 建立一个包含 10 个 chan int 的数组 channelsfor i : range worker_num {channels[i] make(chan int) // 使用内建的 make 构造每一个 channelgo worker(i, channels[i]) // 将 10 个 chan int 分发给 10 个 worker}for i : range worker_num {channels[i] - a i}time.Sleep(time.Millisecond)
}func main() {chanDemo()
}
输出的结果如下因操作设备而异
Worker 0 received a
Worker 2 received c
Worker 1 received b
Worker 3 received d
Worker 9 received j
Worker 4 received e
Worker 5 received f
Worker 6 received g
Worker 7 received h
Worker 8 received ichannel 除了可以作为参数、可以存储在数组当中channel 还可以作为返回值
package mainimport (fmttime
)func createWorker(id int) chan- int { // 返回值 chan int 用于发数据, 因此添加 - 告知使用者// 实际上这个函数的执行相当快: 新建一个 chan int, 并开启一个由匿名函数构成的 goroutine, 之后就返回建立的 chan int// goroutine 将会一直运行下去c : make(chan int) // 在函数中创建 channel, 它也是返回值go func() { // 开一个 goroutine, 来接受 channel 得到的内容for {n : -c // 从 channel 收数据fmt.Printf(Worker %d received %c\n, id, n)}}()return c
}const worker_num int 10func chanDemo() {var channels [10]chan- int // 建立一个包含 10 个 chan int 的数组 channelsfor i : range worker_num {channels[i] createWorker(i)}for i : range worker_num {channels[i] - a i}for i : range worker_num {channels[i] - A i}time.Sleep(time.Millisecond)
}func main() {chanDemo()
}
输出的结果为
Worker 0 received a
Worker 0 received A
Worker 4 received e
Worker 9 received j
Worker 6 received g
Worker 7 received h
Worker 8 received i
Worker 2 received c
Worker 1 received b
Worker 1 received B
Worker 5 received f
Worker 3 received d
Worker 3 received D
Worker 2 received C
Worker 6 received G
Worker 4 received E
Worker 5 received F
Worker 7 received H
Worker 8 received I
Worker 9 received JbufferedChannel
之前我们提到过建立一个 channel 之后如果向 channel 当中写入数据则应该有一个 goroutine 负责接收数据即在 go func 的函数体内进行 n : - channel。
比如运行下述代码
func bufferedChannel() {c : make(chan int)c - 1
}func main() {bufferedChannel()
}将会产生如下的错误信息 可以为 channel 加入一个缓冲区来解决上述问题
func bufferedChannel() {c : make(chan int, 3) // 在 make 当中进一步输入参数 3, 代表当前缓冲区的大小设置为 3c - 1c - 2c - 3
}func main() {bufferedChannel()
}
此时程序可以正常运行不会 deadlock如果进一步发送 4才会 deadlock。
现在输出 channel 当中的数据
func worker(id int, c chan int) {for {n : -c // 从 channel 收数据fmt.Printf(Worker %d received %d\n, id, n)}
}func bufferedChannel() {c : make(chan int, 3) // 在 make 当中进一步输入参数 3, 代表当前缓冲区的大小设置为 3go worker(0, c)c - 1c - 2c - 3c - 4time.Sleep(time.Millisecond)
}func main() {bufferedChannel()
}输出的结果为
Worker 0 received 1
Worker 0 received 2
Worker 0 received 3
Worker 0 received 4我们还想要知道什么时候 channel 当中的数据发完了。应该由发送方来通知接收方没有新的数据要发送了
func channelClose() {c : make(chan int)go worker(0, c)c - 1c - 2c - 3c - 4close(c) // 告诉接受方, 数据发完了time.Sleep(time.Millisecond)
}func main() {channelClose()
}输出的结果如下
Worker 0 received 1
Worker 0 received 2
Worker 0 received 3
Worker 0 received 4
Worker 0 received 0
Worker 0 received 0
Worker 0 received 0
Worker 0 received 0
Worker 0 received 0
Worker 0 received 0
Worker 0 received 0
Worker 0 received 0
Worker 0 received 0
Worker 0 received 0
... (Worker 0 received 0 repeated ...)当 channel 通过 close 通知 goroutine 没有数据要发送了的时候goroutine 仍然会接收数据只不过接受的数据是零值。在 goroutine 接收 channel 的数据时可以使用两个返回值 n 和 ok 来判断 channel 是否 close因此我们对 worker 进行修改
func worker(id int, c chan int) {for {n, ok : -c // 使用两个值来判断 channel 是否 closeif !ok {break}fmt.Printf(Worker %d received %d\n, id, n)}
}此时的输出为
Worker 0 received 1
Worker 0 received 2
Worker 0 received 3
Worker 0 received 4另一种判断 channel 是否 close 的方式是在 for 循环使用
func worker(id int, c chan int) {for n : range c {fmt.Printf(Worker %d received %d\n, id, n)}
}可以得到相同的结果。
总结上述学习到的内容
本节介绍了 channel 及其定义、发送、接收数据的方法如何定义以及使用 bufferd channel在 make 内置方法构造 channel 时显式地指定 buffer 的大小即可在接收数据时使用 range 可以方便地判断 channel 是否关闭。
为什么使用 Channel原因是不要通过共享内存来通信而是通过通信来共享内存。
通过共享内存来通信的典型例子是两个线程之间通过一个 flag 变量来判断事务是否完成。