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的主要内容,如果未能解决你的问题,请参考以下文章

Go 并发编程之协程及其调度机制

7Python全栈之路系列之协程

Go语言之gorountine与管道初体验

Go语言协程并发---管道信号量应用

golang并发编程之channel

python之协程与IO操作