通道方向示例package mainimport "fmt"// 当使用通道作为函数的参数时,你可以指定这个通道是不是只用来发送或者接收值 。// 这个特性提升了程序的类型安全性 。func ping(pings chan <- string, msg string) { // ping 函数定义了一个只允许发送数据的通道 。// 尝试使用这个通道来接收数据将会得到一个编译时错误 。pings <- msg}func pong(pings <-chan string, pongs chan <- string) { // pong 函数允许通道(pings)来接收数据,另一通道(pongs)来发送数据 。msg := <-pings pongs <- msg}func main() { pings := make(chan string, 1) pongs := make(chan string, 1) ping(pings, "passed message") pong(pings, pongs) fmt.Println(<-pongs)}
Go 遍历通道与关闭通道Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片 。格式如下:
v, ok := <-ch
如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭 。
通道选择器(select)select {case <-ch1:// ...case x := <-ch2:// ...use x...case ch3 <- y:// ...default:// ...}
select语句的一般形式 。和switch语句稍微有点相似,也会有几个case和最后的default选择支 。每一个case代表一个通信操作(在某个channel上进行发送或者接收)并且会包含一些语句组成的一个语句块 。一个接收表达式可能只包含接收表达式自身(译注:不把接收到的值赋值给变量什么的),就像上面的第一个case,或者包含在一个简短的变量声明中,像第二个case里一样;第二种形式让你能够引用接收到的值 。
select会等待case中有能够执行的case时去执行 。当条件满足时,select才会去通信并执行case之后的语句;这时候其它通信是不会执行的 。一个没有任何case的select语句写作select{},会永远地等待下去 。
示例:
package mainimport "time"import "fmt"// Go 的通道选择器 让你可以同时等待多个通道操作 。// Go 协程和通道以及选择器的结合是 Go 的一个强大特性 。func main() { // 在我们的例子中,我们将从两个通道中选择 。c1 := make(chan string) c2 := make(chan string) // 各个通道将在若干时间后接收一个值,这个用来模拟例如并行的 Go 协程中阻塞的 RPC 操作 go func() {time.Sleep(time.Second * 1)c1 <- "one" }() go func() {time.Sleep(time.Second * 2)c2 <- "two" }() // 我们使用 select 关键字来同时等待这两个值,并打印各自接收到的值 。for i := 0; i < 2; i++ {select {case msg1 := <-c1:fmt.Println("received", msg1)case msg2 := <-c2:fmt.Println("received", msg2)} } // 我们首先接收到值 "one",然后就是预料中的 "two"了 。// 注意从第一次和第二次 Sleeps 并发执行,总共仅运行了两秒左右 。}
超时实现package mainimport "time"import "fmt"// 超时 对于一个连接外部资源,或者其它一些需要花费执行时间的操作的程序而言是很重要的 。// 得益于通道和 select,在 Go中实现超时操作是简洁而优雅的 。func main() { c1 := make(chan string, 1) // 在我们的例子中,假如我们执行一个外部调用,并在 2 秒后通过通道 c1 返回它的执行结果 。go func() {time.Sleep(time.Second * 2)c1 <- "result 1" }() // 这里是使用 select 实现一个超时操作 。res := <- c1 等待结果,<-Time.After 等待超时时间 1 秒后发送的值 。// 由于 select 默认处理第一个已准备好的接收操作,如果这个操作超过了允许的 1 秒的话,将会执行超时 case 。select { case res := <-c1:fmt.Println(res) case <-time.After(time.Second * 1):fmt.Println("timeout 1") } // 如果我允许一个长一点的超时时间 3 秒,将会成功的从 c2接收到值,并且打印出结果 。c2 := make(chan string, 1) go func() {time.Sleep(time.Second * 2)c2 <- "result 2" }() select { case res := <-c2:fmt.Println(res) case <-time.After(time.Second * 3):fmt.Println("timeout 2") } // 运行这个程序,首先显示运行超时的操作,然后是成功接收的 。// 使用这个 select 超时方式,需要使用通道传递结果 。// 这对于一般情况是个好的方式,因为其他重要的 Go 特性是基于通道和select 的 。}
非阻塞选择器package mainimport ( "fmt")// 常规的通过通道发送和接收数据是阻塞的 。// 然而,我们可以使用带一个 default 子句的 select 来实现非阻塞 的发送、接收,甚至是非阻塞的多路 select 。func main() { messages := make(chan string) signals := make(chan bool) // 这里是一个非阻塞接收的例子 。// 如果在 messages 中存在,然后 select 将这个值带入 <-messages case中 。// 如果不是,就直接到 default 分支中 。select { case msg := <-messages:fmt.Println("received message", msg) default:fmt.Println("no message received") } // 一个非阻塞发送的实现方法和上面一样 。msg := "hi" select { case messages <- msg:fmt.Println("sent message", msg) default:fmt.Println("no message sent") } // 我们可以在 default 前使用多个 case 子句来实现一个多路的非阻塞的选择器 。// 这里我们试图在 messages和 signals 上同时使用非阻塞的接受操作 。select { case msg := <-messages:fmt.Println("received message", msg) case sig := <-signals:fmt.Println("received signal", sig) default:fmt.Println("no activity") }}
推荐阅读
- 阿里+腾讯资深架构师方案-高并发系统下的服务治理
- 华为|华为自研编程语言:下半年见!
- 如何自己编程做游戏 命令提示符大全
- 女大学生|大学女生废话编程爆火!懂不懂编程的看完都拴Q了
- 机器人|萤石发布儿童陪护机器人萤宝RK2遥控编程版:无需识字 3岁就能学编程
- golang编程踩坑记录
- Kali与编程:如何安装KALI LINUX中文拼音输入法?
- 骨折后并发症的食疗方
- 长期卧床的四大并发症
- 结核性腹膜炎最常见的并发症是什么