channel的类型
/*
channel实现同步
*/
package main
import (
"fmt"
"time"
)
//创建一个管道
var ch = make(chan int)
func Print(s string) {
for _, data := range s {
fmt.Printf("%c", data)
time.Sleep(time.Second)
}
fmt.Println("\n")
}
//目标:先打印hello 再打印go
func Person1() {
Print("hello")
ch <- 1 //给管道写数据(发送) "1"只是用于举例
}
func Person2() {
<-ch //从管道取数据(接收),如果通道没有数据,会阻塞
Print("go")
}
func main() {
//新建两个协程
go Person1()
go Person2()
for {
}
}
通过channel实现同步和数据交互
package main
import (
"fmt"
"time"
)
func main() {
//创建管道
ch := make(chan string)
defer fmt.Println("主协程结束")
go func() {
defer fmt.Println("子协程调用完毕")
for i := 0; i < 2; i++ {
fmt.Println("i = ", i)
time.Sleep(time.Second)
}
ch <- "q"
}()
str := <-ch //没有数据前,会阻塞
fmt.Println("str = ", str)
}
无缓冲的channel
package main
import (
"fmt"
"time"
)
func main() {
//创建无缓冲的通道
ch := make(chan int, 0)
//len(ch)缓冲区剩余数据个数,cap(ch)缓冲区大小
fmt.Printf("len(ch) = %d, cap(ch) = %d\n", len(ch), cap(ch))
go func() {
for i := 0; i < 3; i++ {
fmt.Println("子协程:i = ", i)
ch <- i //ch中的数据没有被读出去之前会阻塞在这
}
}()
time.Sleep(2 * time.Second)
for i := 0; i < 3; i++ {
num := <-ch
fmt.Println("num = ", num)
}
}
有缓冲的channel
/*
格式: make(chan type, capacity)
如果给定缓冲区容量,通道是异步的。只要缓冲区有未使用的空间用于发送数据,或者还包括
可以接收的数据,那么其通信就会无阻塞的进行
*/
package main
import (
"fmt"
"time"
)
func main() {
//创建有缓冲的通道
ch := make(chan int, 3)
fmt.Printf("len(ch) = %d, cap(ch) = %d\n", len(ch), cap(ch)) //len(ch):0 cap(ch):3
go func() {
for i := 0; i < 10; i++ {
ch <- i //ch满之后不能再写
fmt.Printf("子协程[%d]:len(ch) = %d, cap(ch) = %d\n", i, len(ch), cap(ch))
}
}()
time.Sleep(2 * time.Second)
for i := 0; i < 10; i++ {
num := <-ch
fmt.Println("num = ", num)
}
}
关闭channel
/*
1. 关闭channel后,无法向channel再发送数据(引发panic错误后导致接收立即返回0)
2. 关闭channel后,可以继续向channel接收数据
3. 对于nil channel,无论收发都会别阻塞
*/
package main
import (
"fmt"
)
func main() {
//创建无缓冲的通道
ch := make(chan int, 0)
go func() {
for i := 0; i < 5; i++ {
ch <- i //写数据
}
//不需要再写数据时,关闭channel
close(ch) //屏蔽此行会死锁 无法跳出下面的死循环
//ch <- 1 //关闭channel后无法再发送数据
}()
//方法1
/*
for {
//如果ok为true,说明ch没有关闭
num, ok := <-ch //读数据
if ok == true {
fmt.Println("num = ", num)
} else {
break
}
}
*/
//方法2
for num := range ch {
fmt.Println("num = ", num)
}
}
单向channel的特性
package main
//import "fmt"
func main() {
//创建一个channel
ch := make(chan int)
//双向channel可以隐式转换为单向channel
var wCh chan<- int = ch //只写
wCh <- 12
//<-wCh
ch <- 12
var rCh <-chan int = ch //只读
<-rCh
//rCh <- 11
//单向是无法转换为双向的
//var ch2 chan int = rCh
}
单向channel的应用
package main
import "fmt"
//此通道只能写不能读
func Producer(wCh chan<- int) {
for i := 0; i < 10; i++ {
wCh <- i * i
}
close(wCh)
}
//只能读 不能写
func Consumer(rCh <-chan int) {
for num := range rCh { //rCh没有数据会阻塞
fmt.Println("num = ", num)
}
}
func main() {
//创建一个双向通道
ch := make(chan int)
//生产者
go Producer(ch) //channel传参是引用传递
//消费者
Consumer(ch)
}
timer
package main
import (
"fmt"
"time"
)
func main() {
//创建一个定时器,设置时间为2s,2s后,往time通道写数据(当前时间)
//时间到了 timer只会响应一次
timer := time.NewTimer(2 * time.Second)
fmt.Println("当前时间:", time.Now())
//2s后,往timer.C写数据,有数据后,就可以读取
t := <-timer.C //channel没有数据前会阻塞
fmt.Println("t = ", t)
}
通过timer实现延时
/*
延时的3种方式
sleep.go
*/
package main
import (
"fmt"
"time"
)
func main() {
//方式1
time.Sleep(2 * time.Second)
//方式2 定时2s 阻塞2s 2s后会产生一个事件 往channel写数据
<-time.After(2 * time.Second)
//方式3
//延时2s后打印一句话
timer := time.NewTimer(2 * time.Second)
<-timer.C //时间未到会阻塞
fmt.Println("时间到")
}
定时器停止和重置
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(3 * time.Second)
b := timer.Reset(1 * time.Second) //定时器重置
fmt.Println("b = ", b)
<-timer.C
fmt.Println("时间到") //3s后打印
}
func main1() {
timer := time.NewTimer(3 * time.Second)
go func() { //创建一个匿名函数作为子协程
<-timer.C //3s过后停止阻塞 执行下面的打印
fmt.Println("定时器时间到,子协程打印")
}()
timer.Stop() //停止定时器
for { //主协程死循环
}
}
Ticker
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(1 * time.Second)
i := 0
for {
<-ticker.C
i++
fmt.Println("i = ", i)
if i == 5 {
ticker.Stop()
break
}
}
}