基础
基本使用
- 带缓冲区channel:定义声明时候制定了缓冲区大小(长度),可以保存多个数据。用于通信
- channel :=make(chan int,3)
- 不带缓冲区channel:只能只能存一个数据,用于两个groutine的同步,阻塞式
- channel :=make(chan bool)
- 只读管道 read_only := make (<-chan int)
只写管道 rite_only := make (chan<- int)
func main() {
//定义
channel :=make(chan int,3)
//存数据
channel <- 1
channel <- 2
channel <- 3
//channel <- 4,报错,容量已经满了
//取数据,如果只想删掉该值,直接<- channel
num :=<- channel
fmt.Println(num)//1
channel <- 4
num2,ok:=<- channel
if ok{
fmt.Println(num2)//2
}
}
注意事项
管道只能存放指定类型数据
- 非缓冲通道上如果发生了流入无流出,或者流出无流入,就会引起死锁。
- 当一个协程存一个协程取,虽然存的快而取得慢,导致容量占满,也不会发生错误,而是负责存的协程会堵塞在channel <- num
- 当一个协程存一个协程取,虽然存的慢而取得快,导致channel为空,也不会发生错误,而是负责取的协程会堵塞在 num := <- channel
- 存满了的channel就不能继续存了,当从存满了的channel取出数据后还可以继续存
当要取某一个值时,要将它前面的值剔除
func main() {
//定义
channel :=make(chan int,3)
//存数据
channel <- 1
channel <- 2
channel <- 3
//取到2,得先将1剔除
<- channel
res,ok:=<-channel
if ok{
fmt.Println(res)
}
}
关闭与遍历
当channel关闭后,只能取不能存
- 方法:close(channel)
- 垃圾回收器会在 channel 不可达时回收它,即使 channel 还未关闭。
- 使用 for range 遍历,同go程中编译器发现channel未被关闭,则会引发deadlock错误
如果channel无数据可读,那么for range会处于等待状态,只有当channel关闭,for range循环才会退出
func main() {
//定义
channel :=make(chan int,10)
//存数据
channel <- 1
channel <- 2
channel <- 3
close(channel)
for k :=range channel{
fmt.Println(k)
}
}
如果采用for循环已经被关闭的管道,当管道没有数据时,读取的数据是管道的默认值,并且循环不会退出。res,ok :=<- channel,其中ok为false
func main() {
channel :=make(chan int,10)
channel <- 1
channel <- 2
channel <- 3
close(channel)
for {
res,ok :=<-channel
if ok {
fmt.Println(res)
}else {
time.Sleep(time.Second)
fmt.Printf("channel无数据,取到的只为%v\n",res)
}
}
}
goroutine&channel
死锁现场一:
func main() {
ch := make(chan int)
<- ch // 阻塞main goroutine, 通道被锁
}
解决:
func main() {
ch := make(chan int)
go func() {
ch<-1
}()
res :=<- ch
fmt.Println(res)
}
注意:groutine一定要在主线程对channel操作前开启,不然主线程阻塞在channel操作造成死锁,
如果上面的管道是有缓冲的,则不会堵死
死锁现场二(无缓冲channel的陷阱):
func main() {
cha, chb := make(chan int), make(chan int)
go func() {
cha <- 1 // cha通道的数据没有被其他goroutine读取走,堵塞当前goroutine
chb <- 0
}()
<- chb // chb 等待数据的写
}
解决:
func main() {
cha, chb := make(chan int), make(chan int)
go func() {
cha <- 1 // cha通道的数据没有被其他goroutine读取走,堵塞当前goroutine
chb <- 0
}()
<- cha //将cha也读出来。因为都是无缓冲的通道,同时也要注意,要严格按照存的顺序来取
<- chb // chb 等待数据的写
}
生产者与消费者(1个send,1个receive)
func producter(apples chan int){
for i:=1;i<=50;i++{
fmt.Println("生产者:我生产了第",i,"个苹果")
apples <- i
}
close(apples)
}
func consumer(apples chan int,flag chan bool){
for {
res ,ok := <- apples
if ok{
fmt.Println("消费者:我吃了第",res,"个苹果")
}else {
break
}
}
flag <- true
close(flag)
}
func main() {
apples := make(chan int,20)
flag := make(chan bool)
go producter(apples)
go consumer(apples,flag)
for{
_ ,ok := <- flag
if ok{
break
}
}
}
多协程求素数(1个send,n个receive)
func PutNum(channel chan int){
for i:=1;i<=100000;i++{
channel <- i
}
close(channel)
}
func PrimeNum(intchan chan int,primechan chan int ,exitchan chan bool){
for{
res,ok :=<- intchan
if ok{
if res>2{
flag:=true
for i:=2;i<res;i++{
if res % i ==0{
flag=false
break
}
}
if flag{
primechan <-res
}
}
}else {
break
}
}
exitchan <-true
fmt.Println("一个线程已执行完毕")
}
func main() {
intchan :=make(chan int,1000)//数据源管道
primechan :=make(chan int,1000)//结果管道
exitchan :=make(chan bool,6)//退出管道
go PutNum(intchan)
for i:=0;i<6;i++{
go PrimeNum(intchan,primechan,exitchan)
}
go func() {
for i:=0;i<6;i++{
<- exitchan
}
close(primechan)
}()
for{
res,ok:=<-primechan
if !ok{
break
}
fmt.Println(res)
}
}
多协程求素数(n个send,n个receive)
func PutNum(channel chan int ,exitintchan chan<- bool,i int){//exitintchan为只写管道
rand.Seed(time.Now().UnixNano())
for {
value := rand.Intn(1000)
if value ==i{
exitintchan <-true
return
}else {
channel<-value
}
}
}
func PrimeNum(intchan chan int,primechan chan int ,exitchan chan<- bool){
for{
res,ok :=<- intchan
if ok{
if res>2{
flag:=true
for i:=2;i<res;i++{
if res % i ==0{
flag=false
break
}
}
if flag{
primechan <-res
}
}
}else {
break
}
}
exitchan <-true
fmt.Println("一个线程已执行完毕")
}
func main() {
intchan :=make(chan int,1000)//数据源管道
exitintchan :=make(chan bool,4) //数据源退出管道
primechan :=make(chan int,1000)//结果管道
exitchan :=make(chan bool,6)//结果退出管道
for i:=0;i<4;i++{
go PutNum(intchan,exitintchan,i)
}
go func() {
for i:=0;i<4;i++{
<- exitintchan
}
close(intchan)
}()
for i:=0;i<6;i++{
go PrimeNum(intchan,primechan,exitchan)
}
go func() {
for i:=0;i<6;i++{
<- exitchan
}
close(primechan)
}()
for{
res,ok:=<-primechan
if !ok{
break
}
fmt.Println(res)
}
}
future模式
将客户端请求的处理过程从同步改为异步,以便将客户端解放出来,在服务端程序处理期间可以去干点其他事情,最后再来取请求的结果。好处在于整个调用过程中不需要等待,可以充分利用所有的时间片段,提高系统的响应速度。
type query struct {
request chan string
response chan string
}
func exec(q query) {
go func() {
sql :=<- q.request
//用睡眠替代执行耗时
time.Sleep(time.Second)
q.response <- sql +"---我是结果"
}()
}
func main() {
q :=query{make(chan string,1),make(chan string,1)}
go exec(q)
q.request <- "我是请求"
//用睡眠替代其他任务
time.Sleep(time.Second)
fmt.Println(<-q.response)
}