赞
踩
Goroutine: 在 Go 语言中,goroutine 是轻量级的线程。它是 Go 运行时管理的,而不是由操作系统管理。一个 Go 程序可以启动很多 goroutine,每个 goroutine 都是一个独立的执行单元。
Channel: Channel 是 Go 语言中的一种数据类型,用于在 goroutine 之间传递数据。Channel 可以用于在 goroutine 之间同步数据,或者用于在 goroutine 之间传递数据
Goroutine 是 Go 语言的并发执行单元,它们是轻量级的线程。
Goroutine 由 Go 运行时管理,而不是操作系统。
Goroutine 的创建和调度都是由 Go 运行时自动管理的。
Goroutine 的调度是非确定性的,这意味着它们的执行顺序是不确定的。
Goroutine 的栈大小是可变的,可以根据需要动态调整。
Channel 是 Go 语言中的数据类型,用于在 goroutine 之间传递数据。
Channel 可以用于在 goroutine 之间同步数据,或者用于在 goroutine 之间传递数据。
Channel 的发送和接收操作都是阻塞的。
Channel 是线程安全的,这意味着多个 goroutine 可以安全地向同一个 channel 发送和接收数据。
Channel 可以使用 select 语句进行非阻塞的操作。
Channel 可以使用 close 函数关闭,这将导致向已关闭的 channel 发送数据会导致 panic,而从已关闭的 channel 接收数据会返回零值和一个 false。
这些特性使得 Go 语言的并发编程模型具有高度的灵活性和强大性,使得开发者可以轻松地编写出高性能、高并发的应用程序。
本文将通过goroutine与channel实现并发处理任务的经典题目,从而加深对goroutine与channel的理解。
题目:计算出50万以内的素数。用普通方法后,再考虑用goroutine加快处理速度。
题目要求
即轮询1-50万的正整数,依次计算出素数
package main import ( "fmt" "time" ) // 判断一个数是否是素数 func isPrieme(num int) bool { for i:=2;i<num;i++{ if num%i == 0 { return false } } return true } func main() { //ch := make(chan int) //创建一个无缓存channel start := time.Now().Unix() for i:=1;i<500000;i++{ if isPrieme(i) { //fmt.Println(i) } } totolTime := time.Now().Unix() - start fmt.Println("用时(s):",totolTime) }
执行结果
(base) ➜ study go run test.go
用时(s): 71
需要借用三个管道来完成题目
package main import ( "fmt" "runtime" "time" ) /* 1. 用一个协程,将1-100个数放入一个channel inChannel中。 2. 启动10个协程,同时从inChannel中取数据,并计算n的平方,将结果值放入一个新的channel resultChannel 3. 10个协程,写完后,放入一个标记位True,到标记channel flagChannel,这个channel用于标识已经完成数据处理 4. 当标记位个数为10时,表示10个协程都不能取数据了(inChannel已经空了),这时才可以关闭管道resultChannel */ // 判断一个数是否是素数 func isPrieme(num int) bool { for i:=2;i<num;i++{ if num%i == 0 { return false } } return true } func writeData(inChannel chan int){ // 写入100个数据到inChannel for i:=1;i<=500000;i++{ inChannel <- i //fmt.Println("写入数据data= ",i) } // 写入完成后关闭inChannel close(inChannel) } func readAndDealData(inChannel chan int,resultChannel chan int, flagChannel chan bool){ fmt.Println("启动一个新的协程------------") for { // 从inChannel中拿数据 data,ok := <-inChannel if ok { // 如果拿到数据 //fmt.Println("拿到数据data= ",data) // 对拿到的数据进行处理 if isPrieme(data) { // 处理完毕后,将结果放入结果管道resultChannel resultChannel<- data //fmt.Println("素数=",data) } }else { // 如果没有从inChannel拿到数据,表示这个协程的工作已经结束 break } } // 拿不到数据则表示这个协程已经处理完成,则放一个标记位到flagChannel管道 flagChannel <- true } func main(){ fmt.Println("hello world") // 设置可用处理器个数 cpuNum := runtime.NumCPU() runtime.GOMAXPROCS(cpuNum-1) fmt.Println("cpuNum=",cpuNum) // 启动协程的数量 gorutNum := 8 //1 --- 69,2 --- 35s,4 --- 21 // 保存输入数据 inChannel := make(chan int, 10000) // 保存计算结果 resultChannel := make(chan int, 100000) // 保存退出标记位,如果flagChannel一旦有数据,标识已经处理完成,主进程检查到后就退出 flagChannel := make(chan bool, gorutNum) start := time.Now().Unix() // 启动写数据的协程 go writeData(inChannel) // 启动10个同时拿数据并处理数据的协程 for i:=0;i<gorutNum;i++ { go readAndDealData(inChannel,resultChannel,flagChannel) } // 阻塞主进程,等待所有协程完成 for i:=0;i<gorutNum;i++ { _,ok := <-flagChannel if ok { // 一旦有数据,表示所有协程已经处理完成 fmt.Println("一个协程处理完毕!") } } close(flagChannel) fmt.Println("Done!") totolTime := time.Now().Unix() - start fmt.Println("总共用时(s): ", totolTime ) }
执行结果
(base) ➜ 04 go run main.go hello world cpuNum= 8 启动一个新的协程------------ 启动一个新的协程------------ 启动一个新的协程------------ 启动一个新的协程------------ 启动一个新的协程------------ 启动一个新的协程------------ 启动一个新的协程------------ 启动一个新的协程------------ 一个协程处理完毕! 一个协程处理完毕! 一个协程处理完毕! 一个协程处理完毕! 一个协程处理完毕! 一个协程处理完毕! 一个协程处理完毕! 一个协程处理完毕! Done! 总共用时(s): 15
可以看出,使用多协程方式来计算50万以内的素数只用了15秒
测试机 | 多协程方式 | 普通方式 |
---|---|---|
macbook i7 | 15秒 | 71秒 |
所以看处理大数据计算密集时可以考虑用goroutine方式。但是从编码难度上考虑,goroutine方式要比普通方式要大一些。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。