Go语言channel
Posted 行走的皮卡丘
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言channel相关的知识,希望对你有一定的参考价值。
Go语言channel
1、什么是channel
channel
是golang在goroutine
之间的通讯方式channel
是引用类型,使用的时候必须通过make进行初始化,make
的channel
打印结果是地址
。
2、为什么需要channel
全局变量加锁的方式来解决goroutine通讯的方式不完美,主线程在等待所有goroutine全部完成的时间很难确定,所以这里需要一个管道channel来完成这种通讯连接。
3、channel的基本介绍
-
1、
channe
l的本质就是一个数据结果-队列 -
2、数据是先进先出
-
3、线程安全,多个
goroutine
访问时,不需要加锁,也就是channel
本身就是线程安全的(因为管道本身就是阻塞模式的,遵循先进先出的概念,所以channel本身就是安全的) -
4、示意图
- 5、channel的注意事项
channel
中只能存放指定的数据类型channle
的数据放满后,就不能再放入了- 如果从 channel 取出数据后,可以继续放入
- 在没有使用协程的情况下,如果
channel
数据取完了,再取,就会报dead lock
channel
接收参数结束后,必须关闭,避免channel
不断增加带来的内存泄漏- 关闭channel的时候不要在接收端关闭
channel
- 6、理解和总结
channel
是引用类型的,也就是指针类型,打印它的结果是个地址,channel
的数据结构是队列,遵循的规律是先进先出,channel
是线程安全的,本身就有锁,读的时候会等待当前接收信号完成后,才会执行发送信号channel
,channel
本身不限定类型。
引用类型的例子
package main
import "fmt"
func main()
var intChan chan int
intChan = make(chan int, 3)
//2. 看看 intChan 是什么
fmt.Printf("intChan 的值=%v intChan 本身的地址=%p\\n", intChan, &intChan)
运行结果:
上面例子可以看出intChan是地址,输出地址的时候,指向另一片地址。也即是它是引用类型。
4、channel的使用
4.1、概念
channel
是golang在goroutine
之间的通讯方式,多个channel
之间传递消息信息,如果说goroutine
是go的并发实体,channel
就是他们之间的连接。
4.2、创建channel
make(chan 元素类型, 容量)
(1)创建channel
ch := make(chan int, 3) //申明channel
(2)发送
ch <- 10 //把10传递给ch
(3)接收
x := <-ch //从ch中接收值并赋值给变量x
(4)关闭管道
close(ch) //关闭管道
(5)实例分析
4.3、实例分析
package main
import "fmt"
func main()
chan1 := make(chan int, 3)
chan1 <- 11
chan1 <- 20
chan1 <- 30
a := <-chan1
fmt.Println(a) //打印的结果是11
解析:通道的发送和接收是1对1的,有几个接收就有几个发送。
执行结果:
package main
import "fmt"
func main()
chan1 := make(chan int, 3)
chan1 <- 11
chan1 <- 20
chan1 <- 30
a := <-chan1
fmt.Println(a) //11
b := <-chan1
fmt.Println(b) //20
c := <-chan1
fmt.Println(c) //30
执行结果:
4.3.1、channel模拟消息队列
package main
import (
"fmt"
)
func main()
fmt.Println("run in main coroutine.")
count := 10
c := make(chan bool, count)
for i := 0; i < count; i++
go func(i int)
fmt.Printf("run in child coroutine %d.\\n", i)
c <- true
(i)
for i := 0; i < count; i++
<-c
执行结果:
4.3.2、区分缓冲和非缓冲
chInt := make(chan int) //非缓冲通道
chBool := make(chan bool, 0) //非缓冲通道
chStr := make(chan string, 2) //缓冲通道
理解:如果申明的是非缓冲的channel,必须在main以外的goroutine里写入,否则会报错。
如:
package main
import "fmt"
func main()
ch1 := make(chan string)
ch1 <- "hello"
fmt.Println(<-ch1)
//报错:fatal error: all goroutines are asleep - deadlock!
// 致命的错误,所有goroutines都在休眠状态-死锁
//我们可以理解为,非缓冲channel必须另起一个groutine才能启动
综上所述必须另起一个groutine才可以:
package main
import "fmt"
func main()
ch1 := make(chan string)
go func()
ch1 <- "hello"
()
fmt.Println(<-ch1)
//输出结果hello
如果是缓冲channel同样也不报错。
package main
import "fmt"
func main()
ch1 := make(chan string, 1)
ch1 <- "hello"
fmt.Println(<-ch1)
//输出结果hello
非缓冲通道必须同时有两个groutine对它读和写
4.3.3、单向接口的遍历
package main
import "fmt"
func recive(ch1 chan <- int)
for i := 1; i<=5; i++
ch1 <- i
close(ch1) //这一句去掉将会报错
func customer(ch1 <- chan int)
for data := range ch1
fmt.Println(data)
func main()
c := make(chan int)
//创建协程
go recive(c)
customer(c)
fmt.Println("done")
//遍历的时候必须关闭当前的channel,否则系统不知道什么时候终止遍历的状态
管道重新赋值后,发现改变了原有的,这是因为,通道本身是引用类型的缘故
func main()
ch1 := make(chan int, 4)
ch1 <- 30
ch1 <- 40
ch1 <- 50
ch2 := ch1
ch2 <- 100
<-ch1
<-ch1
<-ch1
d := <-ch1
fmt.Println(d)
4.4、关于close通道
close关闭通道是必须的,避免其它进程针对当前这次的channel继续进行塞入数据而出现的混淆数据、内存溢出的现象,所以无论是缓冲还是非缓存,在接收数据完成后,都进行close一下
var ch1 = make(chan int, 10)
for i := 1; i <=10; i++
ch1 <- i
close(ch1)//写入完成后关闭
如果未关闭,下个用户继续访问,而channel的内部是个队列数据结构的,会不断的往队列里填充数据的。
5、for … select操作类似于swtich
package main
import "fmt"
func toString(ch chan string)
fmt.Println("to string")
ch <- "hello"
func toInt(ch chan int)
fmt.Println("to int")
ch <- 100
func main()
chI := make(chan int)
chS := make(chan string)
go toString(chS)
go toInt(chI)
for i := 0; i < 2; i++
select
case <- chI:
fmt.Println("get value from int")
case <- chS:
fmt.Println("get value from string")
/*
** 打印结果:
** to int
** get value from int
** to string
** get value from string
*/
6、channel循环遍历的坑
channel循环中的变量不关闭为什么报错?
package main
import "fmt"
func recive(ch1 chan <- int)
for i := 1; i<=5; i++
ch1 <- i
//close(ch1)
func customer(ch1 <- chan int)
for data := range ch1 //管道遍历的时候,是没有key的
fmt.Println(data)
func main()
c := make(chan int)
//创建协程
go recive(c)
customer(c)
fmt.Println("done")
//运行的时候报错
//fatal error: all goroutines are asleep - deadlock!
//goroutine 1 [chan receive]:
//main.customer(0xc00008c060)
recive方法里加上close(ch1)后才运行正常,
实际上我没理解for range channel
for range channel不知道channel实际有多少个元素,我以为是我往里面接收多少数据,它就能遍历多少元素出来,实际上,不关闭channel的接收,它会一直遍历下去。当它遍历到第6个元素的时候就已经阻塞了当前的main函数
基于此我们改一下:
package main
import "fmt"
func recive(ch1 chan <- int)
for i := 1; i<=5; i++
ch1 <- i
func customer(ch1 <- chan int)
for i := 1; i<=5; i++
fmt.Println(<-ch1)
func main()
c := make(chan int)
//创建协程
go recive(c)
customer(c)
fmt.Println("done")
这样就不报错了,实际中注入写入完成后注意关闭channel通道,做到避免死锁的产生。
以上是关于Go语言channel的主要内容,如果未能解决你的问题,请参考以下文章