計算出50萬以内的素數。用普通方法後,再考慮用goroutine加快處理速度。
說明: 本例是一個使用goroutine與channel比較綜合的題目,有一定複雜度,需要先仔細分析,有思路後才上代碼。
代碼實現即輪詢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
需要借用三個管道來完成題目
1. 用一個協程,将50萬個數放入一個管道inChannel中,放入完成後關閉管道。
2. 啟動多個協程,同時從inChannel中取數據,并判斷是否為素數,如果是素數則将結果放入另一個管道 resultChannel
3. 當某個協程完成操作後,放入一個标記位True,到一個叫flagChannel的管道。這個channel用于标識某個協程已經完成退出,當flagChannel管道内的元素個數等于啟動的協程數量時,表示所有協程都完成并退出了。
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 |
71秒 |
15秒 |
所以看處理大數據計算密集時可以考慮用goroutine方式。但是從編碼難度上考慮,goroutine方式要比普通方式要大一些。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!