Go语言之协程gorouinte与管道channel
Posted 程序彤
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言之协程gorouinte与管道channel相关的知识,希望对你有一定的参考价值。
管道之间的通讯
写方法延迟3秒,读方法瞬间执行有一次,故显得很急,刚写一个就读出来了,故intChan管道永远长度都为0。
package main
import (
"fmt"
"time"
)
/*
向intChan管道存入从0逐渐递增的自然数
*/
func storeData(intChan chan int){
for i := 0; i < 10; i++ { // 写10个数据,管道
// 向管道存
intChan <- i
fmt.Println("storeData存了",i)
time.Sleep(time.Second*3)
}
close(intChan) // 关闭
}
/*
从intChan管道取出所有自然数,过后管道为空吧
*/
func takeData(intChan chan int,exitChan chan bool){
for{
v,ok := <-intChan // 从管道取
if !ok{ // 如果取不到,则退出
break
}
fmt.Printf("takeData读到的数据%v---",v)
fmt.Println("intChan当前里面的个数len:",len(intChan))
//time.Sleep(time.Second*2)
// 知道取完才退出。这里并不是死循环,因为有if !ok基准判断
// 如果向管道中存完数据不关闭管道,这里读到尽头将会发生死锁。
}
exitChan <- true // 向exitChan管道中存入true作为标志位,代表可读出所有数据可退出了
close(exitChan)
}
func main(){
// 创建两个管道
intChan := make(chan int,50) //
exitChan := make(chan bool,1)
go storeData(intChan)
go takeData(intChan,exitChan)
for {
_,ok := <- exitChan
if !ok { // 直到读出true(当takeChan方法执行完),才退出
break
}
}
}
若写方法瞬间完成,读方法延迟三秒执行一次,故先写完之后,再慢慢读完了。
结论:写读、存取管道的频率无所谓,但必须是动态的
综合应用-统计1-8000之间的素数
多协程:
package main
import (
"fmt"
"time"
)
func main() {
intChan := make(chan int, 1000)
primeChan := make(chan int, 2000)
exitChan := make(chan bool, 4)
start:=time.Now().Nanosecond()
// 开启协程,向intChan放数
go putNum(intChan)
// 启动4个协程,从intChan取数据判断是否是素数,如果是,将该数放到primeChan
for i := 0; i < 16; i++ {
go primeNum(intChan, primeChan, exitChan)
}
go func() {
// 从exitChan取出4个true才结束
for i := 0; i < 16; i++ {
<-exitChan
}
// 此时primeChan仍未关闭
close(primeChan)
}()
// 取出primeChan结果
for {
_, ok := <-primeChan
if !ok { // for循环退出的基准条件
break
}
//fmt.Println("素数为", res)
}
fmt.Println("主线程退出")
end:=time.Now().Nanosecond()
fmt.Println("耗时(纳秒)",end-start)
}
func putNum(intChan chan int) {
for i := 0; i < 8000; i++ {
intChan <- i
//fmt.Println("putNum()向intChan管道存入了数据:", i)
}
// 存完数据关闭管道
close(intChan)
}
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
var flag bool // 默认是素数
for {
num, ok := <-intChan
//time.Sleep(time.Millisecond*10)
if !ok { // 退出外层for循环
break
}
for i := 2; i < num; i++ {
if num%i == 0 { // 说明该num不是素数
flag = false
break // 退出内层for循环
}
}
if flag {
primeChan <- num
}
}
fmt.Println("有一个primeNum协程因为取不到数据,退出!")
// 还不能关闭primeChan,而是给exitChan一个true
exitChan <- true
}
单协程:
package main
import (
"fmt"
"time"
)
func main(){
start := time.Now().Nanosecond()
for num := 0; num < 8000; num++ {
flag := true
for i := 2; i < num; i++ {
if num%2==0 {
flag = false
break
}
}
if flag{
}
}
end := time.Now().Nanosecond()
fmt.Println("普通方法耗时",end-start)
}
不知何时必须close管道时,使用select-case-default
package main
import "fmt"
func main() {
intChan := make(chan int, 10)
for i := 0; i < 10; i++ {
intChan <- i
}
stringChan := make(chan string, 5)
for i := 0; i < 5; i++ {
stringChan <- "hello" + fmt.Sprintf("%d\\n", i)
}
// 如果不close将会导致死锁,不确定何时关闭管道时
for {
select {
case v := <-intChan: // 即使管道中取不到数据,不会阻塞死锁!
fmt.Printf("从intChan读取的数据%d\\n", v)
case v := <-stringChan:
fmt.Printf("从stringChan读取的数据%v", v)
default:
fmt.Println("都取不到,执行其他了!")
//break // 没有用
return
}
}
}
以上是关于Go语言之协程gorouinte与管道channel的主要内容,如果未能解决你的问题,请参考以下文章