二十五、go语言的通道
目录
一、收发通信
二、将通道作为参数传递(读、写、读写)
三、select
1、先收到消息的先执行
2、一直没有收到消息退出通道
3、不知道何时退出情况下退出通道
go语言中的goroutine可以看成线程,但是又不能看成和其它语言一样的线程,因为在go语言中,goroutine之间的通信和其它语言不一样,例如如果在java中多条线程访问一个内存中数据在访问前需要进行加锁避免其它线程进入进行修改,本质上是通过共享了内存进行通信。而在go中是不一样的,在go中是“不要通过共享内存进行通信,而是通过通信来共享内存”
一、收发通信
import ("fmt""time"
)func receiver(c chan string) {for msg := range c {fmt.Println(msg)}
}
func main() {message := make(chan string, 2)message <- "hello"message <- "world"close(message)fmt.Println("push two message to channel")time.Sleep(time.Second * 2)receiver(message)
}
结果在2s中后输出
hello
world
解释
上述案例中 receiver函数接收了一个通道为string值的参数,并且进行循环输出
main方法中,mak(chan string,2)声明创建长度为2的通道
message <- "" 往通道内存入消息
close关闭通道意味着不可以再通过这个通道发送消息
调用receiver并传入message参数
二、将通道作为参数传递(读、写、读写)
在参数中<-在chan左边意味着可读,在右边意味着可写,没有意味着可读可写
func showReader(c <-chan string) {msg := <-cfmt.Println(msg)
}
func showWriter(c chan<- string) {c <- "hello world"
}func showReaderAndWriter(c chan string) {msg := <-cfmt.Println(msg)c <- "hello world"
}
在reader函数中,c是什么我们只能输出什么
在writer函数中,c是什么我们可以修改
在readerAndWriter函数中,c是什么我们可以输出什么也可以指定c是什么
三、select
1、先收到消息的先执行
func ping(c chan int) {time.Sleep(time.Second * 3)c <- 1
}
func ping2(c chan<- int) {time.Sleep(time.Second * 2)c <- 2
}
func main() {channel := make(chan int)channel2 := make(chan int)go ping(channel)go ping2(channel2)select {case msg := <-channel:fmt.Println(msg)case msg := <-channel2:fmt.Println(msg)}
}
结果:
2
解释L
在select中最先收到消息的执行后续代码,当创建两个ping方法时,ping sleep了3秒,ping2 sleep了2秒所以先执行了ping2
2、一直没有收到消息退出通道
当一直没有收到消息可以使用case <-time.After(time.Second * 3):
import ("fmt""time"
)func ping(c chan int) {time.Sleep(time.Second * 5)c <- 1
}
func ping2(c chan<- int) {time.Sleep(time.Second * 4)c <- 2
}
func main() {channel := make(chan int)channel2 := make(chan int)go ping(channel)go ping2(channel2)select {case msg := <-channel:fmt.Println(msg)case msg := <-channel2:fmt.Println(msg)case <-time.After(time.Second * 3):fmt.Println("timeout")}}
3、不知道何时退出情况下退出通道
import ("fmt""time"
)func sender(c chan string) {t := time.NewTicker(1 * time.Second)for {c <- "hello world"<-t.C}
}
func main() {messages := make(chan string)stop := make(chan bool)go sender(messages)go func() {time.Sleep(2 * time.Second)fmt.Println("time up")stop <- true}()for {select {case status := <-stop:fmt.Println(status)returncase msg := <-messages:fmt.Println(msg)}}
}
结果:
hello world
hello world
hello world
time up
true
解释:
创建了一个sender方法,使用time.NewTicker(1 * time.Second),每一秒往通道中传递一次值
t := time.NewTicker(1 * time.Second)
for {c <- "hello world"<-t.C
}
在for循环中执行
case msg := <-messages:fmt.Println(msg)
在接收到消息后执行,那么如果在没有手动终止程序的话就会一直执行
所以创建一个退出通道,当接收到true值时进行return,退出通道
case status := <-stop:fmt.Println(status)return
在同时并发执行一个func,并让这个程序sleep 2秒后,在给通道stop值为true
go func() {time.Sleep(2 * time.Second)fmt.Println("time up")stop <- true}()
所以在第一次并发调用
go sender(messages)
执行了一次
fmt.Println(msg)
此时func开始sleep 2秒,所以又执行了两次,当stop为ture时进行了return退出了通道