什么是信道?
信道可以想像成 Go 协程之间通信的管道。如同管道中的水会从一端流到另一端,通过使用信道,数据也可以从一端发送,在另一端接收。
信道的声明
package main
import (
"fmt"
"time"
)
//不同goroutine之间通信
//通过channel实现
func main() {
//1 定义channel
var c chan int
//2 信道的零值(引用类型,空值为nil,当做参数传递时,不需要取地址,改的就是原来的,需要初始化再使用)
fmt.Println(c)
//3 信道初始化
c=make(chan int) //数字暂时先不关注
//4 信道的放值 (注意赋值和放值)
//c<-1
//c=12 //赋值报错
//5 信道取值
//<-c
//6 取出来赋值给一个变量 int
//var a int
//a=<-c
//a:=<-c
//7 信道默认不管放值还是取值,都是阻塞的
//c是引用类型
go test1(c)
a:=<-c //阻塞 不但实现了两条协程之间通信,还实现了等待协程执行结束
fmt.Println(a)
}
func test1(a chan int) {
fmt.Println("go go go ")
time.Sleep(1*time.Second)
//往信道中放一个值
a<-10 //阻塞
}
信道小例子
package main
//信道小例子
//程序有一个数中 每一位的平方和与立方和,然后把平方和与立方和相加并打印出来
import (
"fmt"
"time"
)
func calcSquares(number int, squareop chan int) {
sum := 0 //总和
for number != 0 {
digit := number % 10 //589对10取余数,9 8 5
sum += digit * digit //sum=9*9 8*8 5*5
number /= 10 //num=58 5 0
}
time.Sleep(2*time.Second)
squareop <- sum
}
func calcCubes(number int, cubeop chan int) {
sum := 0
for number != 0 {
digit := number % 10
sum += digit * digit * digit
number /= 10
}
time.Sleep(1*time.Second)
cubeop <- sum
}
//func calcSquares(number int, squareop chan int) int{
// sum := 0 //总和
// for number != 0 {
// digit := number % 10 //589对10取余数,9 8 5
// sum += digit * digit //sum=9*9 8*8 5*5
// number /= 10 //num=58 5 0
// }
// time.Sleep(2*time.Second)
// return sum
//}
//
//func calcCubes(number int, cubeop chan int) int{
// sum := 0
// for number != 0 {
// digit := number % 10
// sum += digit * digit * digit
// number /= 10
// }
// time.Sleep(2*time.Second)
// return sum
//}
func main() {
ctime:=time.Now().Unix()
fmt.Println(ctime)
number := 589
sqrch := make(chan int)
cubech := make(chan int)
//num1:=calcSquares(number, sqrch)
//num2:=calcCubes(number, cubech)
go calcSquares(number, sqrch)
go calcCubes(number, cubech)
squares, cubes := <-sqrch, <-cubech
//squares:= <-sqrch
//cubes:=<-cubech
fmt.Println("Final output", squares + cubes)
ttime:=time.Now().Unix()
fmt.Println(ttime)
fmt.Println(ttime-ctime)
}
信道的死锁现象
package main
import "fmt"
//信道的死锁现象,默认都是阻塞的,一旦有一个放,没有人取 或者一个人取,没有人放,就会出现死锁
//func main() {
// //var c chan int =make(chan int )
//
// //c<-1 //其实放不进去,阻塞在这,就死锁了
// //<-c //没有,取不到,阻塞在这,就死锁了
//
//}
//单向信道
//func sendData(sendch chan<- int) {
// sendch <- 10
//}
//
//func main() {
// //sendch := make(chan<- int) //定义了一个只写信道
// sendch := make(chan int) //定义了一个可读可写信道
// go sendData(sendch) //传到函数中转成只写信道,在goroutine中,只负责写,不能往外读,主协程读
// fmt.Println(<-sendch) //只写信道一旦读,就有问题
//}
///3 关闭信道
//func main() {
// sendch := make(chan int)
// //关闭信道
// //close(sendch)
//
// //信道可以用for循环循环
//}
// 信道关闭close(sendch) ,for循环循环信道,如果不关闭会报死锁,如果关闭了,放不进去,循环结束
func producer(chnl chan int) {
for i := 0; i < 100; i++ {
fmt.Println("放入了",i)
chnl <- i
}
close(chnl)
}
func main() {
ch := make(chan int)
go producer(ch)
for v := range ch {
fmt.Println("Received ",v)
}
}
缓冲信道
package main
import (
"fmt"
"sync"
"time"
)
//缓冲信道:只在缓冲已满的情况,才会阻塞向缓冲信道,只有在缓冲为空的时候,才会阻塞从缓冲信道接收数据
//func main() {
// var c chan int =make(chan int,6) //无缓冲信道数字是0
// c<-1
// c<-2
// c<-3
// c<-4
// c<-5
// c<-6
// //c<-7 //死锁
// <-c
// <-c
// //<-c
// //<-c
// //<-c
// //<-c
// //<-c // 取空了,死锁(一个goroutine中会出现)
//
//
// // 2 长度 vs 容量
// //fmt.Println(len(c)) //目前放了多少
// //fmt.Println(cap(c)) //可以最多放多少
//
//
//
//}
//3 WaitGroup 等待所有goroutine执行完成
//4 使用信道如何实现?
func process1(i int,wg *sync.WaitGroup) {
fmt.Println("started Goroutine ", i)
time.Sleep(2 * time.Second)
fmt.Printf("Goroutine %d ended
", i)
//一旦有一个完成,减一
wg.Done()
}
func main() {
var wg sync.WaitGroup //没有初始化,值类型,当做参数传递,需要取地址
//fmt.Println(wg)
for i:=0;i<10;i++ {
wg.Add(1) //启动一个goroutine,add加1
go process1(i,&wg)
}
wg.Wait() // 一直阻塞在这,知道调用了10个done,计数器减到零
}