golang goroutine(协程)和 channel(管道) 案例解析

文章目录

goroutine和channel概念

  • goroutine(协程),一般我们常见的是进程,线程,进程可以理解为一个软件在运行执行的过程,线程跟协程比较类似,都是单独开辟一块内存,异步执行。不同的是协程占的内存比较小初始栈2KB左右。
  • channel(管道) , 管道实则也是用来存储数据的,那为什么不用切片呢,channel主要用于多个协程之间的通信同步,遵循的是先进先出,channel有容量的限制,如果说一直往里面存,不取的话那管道就会阻塞报错,所以合理处理管道数据,当数据处理完成后,要将管道关闭,不然后面如果要遍历channel时,就会一直等待channel关闭才会执行完成。

开启线程与channel简单通信流程

这里是使用了sync.awitGroup实现多协程异步执行,与channel进行通信过程。

go 复制代码
//使用awitGroup用于阻塞主进程作用 等待所有协程执行完成,解除阻塞(类似于计数器,计数器为0,解除阻塞)
var wg sync.awitGroup

func printNum (ch chan int){
	for i :=1; i < 10 i++{
	   //循环将i添加到ch管道中,(channel为引用类型,可以直接修改源数据)
		ch <- i
     }
     //关闭管道 在后面我们如果要遍历处理管道数据时,不关闭则接收方会无线阻塞
     close(ch)
     //计数器减1
     wg.Done()
}

func readNum (ch chan int){
	//往ch管道中取数据 打印出来 ,channel时数据消耗性,只要读取了,这条数据不会在channel中保留、
   for v := range ch{
	  fmt,printIn(v)
   }
 }

 func main {
 	//创建一个channel管道,int类型,容量为10(缓冲区为10)
	ch:= make(chan int ,10)

	//计数器加1
	wg.add(1)
	//go关键字 开启一个协程 
	go printNum(ch)
	
	//计数器加1
	wg.add(1)
	//开启一个协程  协程与协程
	go readNum(ch)
    
    //阻塞主进程
    wg.awit()
	fmt.printIn("执行完成")
}

多个工作协程并发执行流程

处理100万个数字内那些是素数,这种数量大的情况下,循环创建多个工作协程,同时执行相关逻辑。

javascript 复制代码
//使用awitGroup用于阻塞主进程作用 等待所有协程执行完成,解除阻塞(类似于计数器,计数器为0,解除阻塞)
var wg sync.awitGroup


func generateNumbers(inputChan chan int){
	for i :=2; i < 1000000 i++{
	   //循环将i添加到ch管道中,(channel为引用类型,可以直接修改源数据)
		inputChan  <- i
     }
     //关闭管道 在后面我们如果要遍历处理管道数据时,不关闭则接收方会无线阻塞
     close(ch)
     //计数器减1
     wg.Done()
}


func worker(inputChan chan int , outputChan chan int,exitChan chan int){
	//inputChan 管道中取数据 打印出来 ,channel是数据消耗性,只要读取了,这条数据不会在channel中保留、所以循环worker线程是随机值,是不可能多个工作线程执行一个数值
   	for num := range inputChan {
		flag := true
		for i := 2; i < num; i++ {
			// 判断如果不为素数 flag为false 则break跳出循环
			if num%i == 0 {
				flag = false
				break
			}
		 }
		 if flag {
			primeChan <- num //num 为素数 放在outputChan 管道中
		 }
	   }
    	// 标记减一  与wg.add(1) 配套使用
	    wg.Done()
	    // 因为worker需要多次执行 所以不能直接关闭outputChan 管道
	    // 向exitChan管道中写入数据 记录退出信号
	    exitChan <- true
 }


func listenWorker(exitChan chan int ,outputChan chan int){
	for i:=1; i < 16; i++{
	/*
	从oututChan中取数据,如果循环过程中执行的比工作协程快,没有取到数据,则会阻塞等待,
	直到outputChan中拿到数据,循环16次,全部协程执行完成。
     */
     <-outputChan
   }
   //关闭outputChan管道
   close(outputChan)
   //结束监听携程
   wg.Done()
}


 func main {
 	//存储100万个待处理数字
	inputChan:= make(chan int ,1000)
	//存素数的channel管道
	outputChan:= make(chan int 1000)
	//监听多协程结束的信号 容量为16,因为我们就开了16个协程,一般为cpu的逻辑处理器数量
	exitChan := make(chat bool 16)
	//计数器加1
	wg.add(1)
	//go关键字 开启一个协程 
	go generateNumbers(inputChan)
	
    // 创建工作协程
    numWorkers := runtime.NumCPU() // 协程数 = CPU 核心数
    for i := 0; i < numWorkers; i++ {
        go worker(inputChan, outputChan)
    }
	
	//如何关闭循环的多协程,第二种方案 再开一协程用于实时监听工作协程工作状态
	wg.add(1)
	go listenWorker(exitChan,outputChan)

    
    //阻塞主进程
    wg.awit()
     //如何关闭循环的多协程,第一种方案 在所有协程完成之后,关闭存素数的管道(outputChan)
	close(outputChan)
	fmt.printIn("执行完成")
}
相关推荐
2601_962071574 小时前
类变量和全局变量的生命周期有什么区别?
java·开发语言·jvm
未来之窗软件服务9 小时前
Go 利器 GoReSym:剥离符号恢复 Go 二进制元数据—东方仙盟
开发语言·golang·仙盟创梦ide·东方仙盟
用户0942485680310 小时前
第23章:OpenJDK逃逸分析、标量替换与锁优化
java·jvm
wuminyu10 小时前
LockStack在虚拟线程Mount和Unmount拷贝过程剖析
java·linux·c语言·jvm·c++
念何架构之路12 小时前
zap WriteSyncer与Sink体系
云原生·golang
microrain14 小时前
权限改了,接口立刻生效,菜单要等重新登录:SagooIoT 权限体系的四道闸门
物联网·golang·开源·sagooiot
福兮说14 小时前
Go time 包的九个坑:== 判断相等、Parse 差 8 小时、1 月 31 日加一个月变成 3 月 3 日
go
2601_9620715715 小时前
类变量和全局变量的隔离性有什么区别?
java·开发语言·jvm
估值探索者17 小时前
【Python量化系统工程实战 #01】数据存储选型 CSVSQLiteMySQL 对比与 SQLite 实战建库
开发语言·jvm·python·sqlite·api接口·数据api接口·股票数据api接口
打工仔折腾 AI1 天前
从 Demo 到生产级 Agent:8 个关键设计机制与 Python 实现拆解
java·jvm·人工智能·后端·python·langchain·ai agent 实战