欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

Go素?cái)?shù)篩選分析

Go素?cái)?shù)篩選分析

1. 素?cái)?shù)篩選介紹

學(xué)習(xí)Go語(yǔ)言的過(guò)程中,遇到素?cái)?shù)篩選的問(wèn)題。這是一個(gè)經(jīng)典的并發(fā)編程問(wèn)題,是某大佬的代碼,短短幾行代碼就實(shí)現(xiàn)了素?cái)?shù)篩選。但是自己看完原理和代碼后一臉懵逼(僅此幾行能實(shí)現(xiàn)素?cái)?shù)篩選),然后在網(wǎng)上查詢相關(guān)資料,依舊似懂非懂。經(jīng)過(guò)1天的分析調(diào)試,目前基本上掌握了的原理。在這里介紹一下學(xué)習(xí)理解的過(guò)程。

目前創(chuàng)新互聯(lián)已為上千余家的企業(yè)提供了網(wǎng)站建設(shè)、域名、雅安服務(wù)器托管、網(wǎng)站托管維護(hù)、企業(yè)網(wǎng)站設(shè)計(jì)、薩迦網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。

素?cái)?shù)篩選基本原理如下圖:

就原理來(lái)說(shuō)還是比較簡(jiǎn)單的,首先生成從 2 開(kāi)始的遞增自然數(shù),然后依次對(duì)生成的第 1, 2, 3, ...個(gè)素?cái)?shù) 整除,經(jīng)過(guò)全部整除仍有余數(shù)的自然數(shù),將會(huì)是素?cái)?shù)。

大佬的代碼如下:

// 返回生成自然數(shù)序列的管道: 2, 3, 4, ...
// GenerateNatural 函數(shù)內(nèi)部啟動(dòng)一個(gè) Goroutine 生產(chǎn)序列,返回對(duì)應(yīng)的管道
func GenerateNatural() chan int {
	ch := make(chan int)
	go func() {
		for i := 2; ; i++ {
			ch <- i
		}
	}()
	return ch
}
// 管道過(guò)濾器: 將輸入序列中是素?cái)?shù)倍數(shù)的數(shù)淘汰,并返回新的管道
// 函數(shù)內(nèi)部啟動(dòng)一個(gè) Goroutine 生產(chǎn)序列,返回過(guò)濾后序列對(duì)應(yīng)的管道
func PrimeFilter(in <-chan int, prime int) chan int {
	out := make(chan int)
	go func() {
		for {
			if i := <-in; i%prime != 0 {
				out <- i
			}
		}
	}()
	return out
}
func main() {
	ch := GenerateNatural() // 自然數(shù)序列: 2, 3, 4, ...
	for i := 0; i < 100; i++ {
		prime := <-ch // 新出現(xiàn)的素?cái)?shù)
		fmt.Printf("%v: %v\n", i+1, prime)
		ch = PrimeFilter(ch, prime) // 基于新素?cái)?shù)構(gòu)造的過(guò)濾器
	}
}

main()函數(shù)先是調(diào)用 GenerateNatural() 生成最原始的從 2 開(kāi)始的自然數(shù)序列。然后開(kāi)始一個(gè) 100 次迭代的循環(huán),希望生成 100 個(gè)素?cái)?shù)。在每次循環(huán)迭代開(kāi)始的時(shí)候,管道中的第一個(gè)數(shù)必定是素?cái)?shù),我們先讀取并打印這個(gè)素?cái)?shù)。然后基于管道中剩余的數(shù)列,并以當(dāng)前取出的素?cái)?shù)為篩子過(guò)濾后面的素?cái)?shù)。不同的素?cái)?shù)篩子對(duì)應(yīng)的管道是串聯(lián)在一起的。

運(yùn)行代碼,程序正確輸出如下:

1: 2
2: 3
3: 5
......
......
98: 521
99: 523
100: 541

2. 代碼分析

之前在課本中學(xué)習(xí)到:chan底層結(jié)構(gòu) 是一個(gè)指針,所以我們能在函數(shù)間直接傳遞 channel,而不用傳遞 channel 的指針。

上述代碼fun GenerateNatural()中創(chuàng)建了管道ch := make(chan int),并創(chuàng)建一個(gè)協(xié)程(為了便于描述,該協(xié)程稱為Gen)持續(xù)向ch中寫(xiě)入漸增自然數(shù)。

當(dāng)i=0時(shí),main()prime := <-ch讀取該ch(此時(shí)prime=2,輸出素?cái)?shù)2),接著將ch傳入PrimeFilter(ch, prime)中。PrimeFilter(ch, prime)創(chuàng)建新協(xié)程(稱為PF(ch, 2))持續(xù)讀取傳入的chch2之前已被取出,從3依次往后讀?。?,同時(shí)返回一個(gè)新的chan out(當(dāng)通過(guò)過(guò)濾器的iout寫(xiě)入時(shí),此時(shí)out僅有寫(xiě)入而沒(méi)有讀取操作,PF(ch, 2)將阻塞在第1次寫(xiě)chan out操作)。與此同時(shí)main()ch = PrimeFilter(ch, 2)out賦值給ch,此操作給ch賦了新變量。到這里,重點(diǎn)來(lái)了:由于在隨后的時(shí)間里,協(xié)程Gen、PF(ch, 2)中仍需要不停寫(xiě)入和讀取ch,這里將out賦值給ch的操作是否會(huì)更改Gen、PF(ch, 2)兩協(xié)程中ch的值了?

直接給出答案(后面會(huì)給出代碼測(cè)試),此時(shí)ch賦新值不影響Gen、PF(ch, 2)兩協(xié)程,僅影響main() for循環(huán)體隨后對(duì)chan的操作。(本人認(rèn)為gochannel參數(shù)傳遞采用了channel指針的拷貝,后續(xù)給channel賦新值相當(dāng)于將該channel重新指向了另外一個(gè)地址,該channel與之前協(xié)程中使用的channel分別指向不同地址,是完全不同的變量)。為了便于后面分析,這里將ch = PrimeFilter(ch, 2)賦值后的ch稱為ch_2。

當(dāng)i=1時(shí),main() for循環(huán)讀取前一次產(chǎn)生新的ch_2賦值給prime(此時(shí)prime=3,輸出素?cái)?shù)3),接著將ch_2傳入PrimeFilter(ch, prime)并創(chuàng)建新協(xié)程(稱為PF(ch, 3)),而后ch = PrimeFilter(ch, 3)將新產(chǎn)生的out賦值給ch,稱為ch_3。與此同時(shí)協(xié)程Gen持續(xù)向ch中寫(xiě)入直至阻塞,攜程PF(ch, 2)持續(xù)讀取ch值并寫(xiě)入ch_2直至阻塞,新協(xié)程PF(ch, 3)持續(xù)讀取ch_2值并輸出至chan out(即ch_3)(此時(shí)ch_3僅有寫(xiě)入而沒(méi)有讀取操作,PF(ch, 3)將阻塞在第1次寫(xiě)ch_3操作)。

當(dāng)i繼續(xù)增加時(shí),后面的結(jié)果以此類推。

總結(jié)一下main()函數(shù)中,每循環(huán)1次,會(huì)增加一個(gè)協(xié)程PF(ch, prime),且協(xié)程Gen與新增加的協(xié)程之間是串聯(lián)的關(guān)系(即前一個(gè)協(xié)程的輸出,作為下一個(gè)協(xié)程的輸入,二者通過(guò)channel交互),協(xié)程main每次循環(huán)讀取最后一個(gè)channel的第1個(gè)值,獲取prime素?cái)?shù)?;驹砣缦聢D所示。

3. 代碼驗(yàn)證

(1) channel參數(shù)傳遞驗(yàn)證

func main() {
	ch1 := make(chan int)
	go write(ch1)
	go read(ch1)
	time.Sleep(time.Second * 3)
	fmt.Println("main() 1", ch1)
	ch2 := make(chan int)
        ch1 = ch2
	fmt.Println("main() 2", ch1)
	time.Sleep(time.Second * 3)
}

func read(ch1 chan int) {
	for {
		time.Sleep(time.Second)
		fmt.Println("read", <-ch1, ch1)
	}
}
func write(ch1 chan int) {
	for {
		time.Sleep(time.Second)
		fmt.Println("write", ch1)
		ch1 <- 5
	}
}

測(cè)試代碼比較簡(jiǎn)單,在main()中創(chuàng)建chan ch1,后創(chuàng)建兩個(gè)協(xié)程write、read分別對(duì)ch1不間斷寫(xiě)入與讀取,持續(xù)一段時(shí)間后,main()新創(chuàng)建ch2,并賦值給ch1,查看協(xié)程write、read是否受到影響。

...
write 0xc0000
read 5 0xc0000
main() 1 0xc0000
main() 2 0xc000
write 0xc0000
read 5 0xc0000
...

程序輸出如上,可以看到ch1地址為0xc0000,ch2地址為0xc000。main()ch1的重新賦值不會(huì)影響到其他協(xié)程對(duì)ch1的讀寫(xiě)。

(2) 素?cái)?shù)篩選代碼驗(yàn)證

在之前素?cái)?shù)篩選源碼的基礎(chǔ)上,添加一些調(diào)試打印代碼,以便更容易分析代碼,如下所示。

package main

import (
   "fmt"
   "runtime"
   "sync/atomic"
)

var total uint32

// 返回生成自然數(shù)序列的管道: 2, 3, 4, ...
func GenerateNatural() chan int {
   ch := make(chan int)
   go func() {
      goRoutineId := atomic.AddUint32(&total, 1)
      for i := 2; ; i++ {
         //fmt.Println("before generate", i)
         ch <- i
         fmt.Printf("[routineId: %.4v]----generate i=%v, ch=%v\n", goRoutineId, i, ch)
      }
   }()
   return ch
}

// 管道過(guò)濾器: 刪除能被素?cái)?shù)整除的數(shù)
func PrimeFilter(in <-chan int, prime int) chan int {
   out := make(chan int)
   go func() {
      goRoutineId := atomic.AddUint32(&total, 1)
      for {
         i := <-in
         if i%prime != 0 {
            fmt.Printf("[routineId: %.4v]----read i=%v, in=%v, out=%v\n", goRoutineId, i, in, out)
            out <- i
         }
      }
   }()
   return out
}

func main() {
   goRoutineId := atomic.AddUint32(&total, 1)
   ch := GenerateNatural() // 自然數(shù)序列: 2, 3, 4, ...
   for i := 0; i < 100; i++ {
      //fmt.Println("--------before read prime")
      prime := <-ch // 新出現(xiàn)的素?cái)?shù)
      fmt.Printf("[routineId: %.4v]----main i=%v; prime=%v, ch=%v, total=%v\n", goRoutineId, i+1, prime, ch, runtime.NumGoroutine())
      ch = PrimeFilter(ch, prime) // 基于新素?cái)?shù)構(gòu)造的過(guò)濾器
   }
}

1)打印協(xié)程id

由于Go語(yǔ)言沒(méi)有直接把獲取goid的接口暴露出來(lái),這里采用atomic.AddUint32原子操作,每次新建1個(gè)協(xié)程時(shí),將atomic.AddUint32(&total, 1)的值保存下來(lái),作為該協(xié)程的唯一id。

2)輸出結(jié)果分析

[routineId: 0002]----generate i=2, ch=0xc0000
[routineId: 0001]----main i=1; prime=2, ch=0xc0000, total=2
[routineId: 0003]----read i=3, in=0xc0000, out=0xc0000
[routineId: 0002]----generate i=3, ch=0xc0000
[routineId: 0001]----main i=2; prime=3, ch=0xc0000, total=3
[routineId: 0002]----generate i=4, ch=0xc0000
[routineId: 0002]----generate i=5, ch=0xc0000
[routineId: 0003]----read i=5, in=0xc0000, out=0xc0000
[routineId: 0002]----generate i=6, ch=0xc0000
[routineId: 0002]----generate i=7, ch=0xc0000
......

輸出結(jié)果如上,main協(xié)程id=1,GenerateNatural協(xié)程id=2,PrimeFilter(ch, prime)協(xié)程id3開(kāi)始遞增。這里還是不太容易看明白,下面分類闡述輸出結(jié)果。

首先,單獨(dú)查看GenerateNatural協(xié)程輸出,如下??梢钥闯?,此協(xié)程就是在寫(xiě)入阻塞交替間往ch=0xc0000中寫(xiě)入數(shù)據(jù)。

[routineId: 0002]----generate i=2, ch=0xc0000
[routineId: 0002]----generate i=3, ch=0xc0000
[routineId: 0002]----generate i=4, ch=0xc0000
[routineId: 0002]----generate i=5, ch=0xc0000
[routineId: 0002]----generate i=6, ch=0xc0000
[routineId: 0002]----generate i=7, ch=0xc0000
[routineId: 0002]----generate i=8, ch=0xc0000
[routineId: 0002]----generate i=9, ch=0xc0000
......

接著,查看PrimeFilter(ch, prime)協(xié)程,如下。每輸出1個(gè)素?cái)?shù),將增加1個(gè)PrimeFilter(ch, prime)協(xié)程,且協(xié)程id號(hào)從3開(kāi)始遞增。

[routineId: 0003]----read i=3, in=0xc0000, out=0xc0000
......
[routineId: 0004]----read i=5, in=0xc0000, out=0xc0000181e0
......
[routineId: 0005]----read i=7, in=0xc0000181e0, out=0xc00020a000
......
[routineId: 0006]----read i=11, in=0xc00020a000, out=0xc00020a060
......

可以看出,協(xié)程[routineId: 0003]讀取GenerateNatural協(xié)程ch=0xc0000值作為輸入,并將out=0xc0000輸出作為[routineId: 0004]協(xié)程輸入。以此類推,從id>=2開(kāi)始的多個(gè)協(xié)程是通過(guò)channel管道串聯(lián)在一起的,且前一個(gè)協(xié)程的輸出作為后一個(gè)協(xié)程的輸入。與前述分析一致。

最后,查看main線程,其id=1,可見(jiàn)main每次循環(huán)讀取最后一個(gè)channel的第1個(gè)值,且該值為素?cái)?shù)。與前述分析一致。

[routineId: 0002]----generate i=2, ch=0xc0000
[routineId: 0001]----main i=1; prime=2, ch=0xc0000, total=2
[routineId: 0003]----read i=3, in=0xc0000, out=0xc0000
......
[routineId: 0001]----main i=2; prime=3, ch=0xc0000, total=3
......
[routineId: 0004]----read i=5, in=0xc0000, out=0xc0000181e0
......
[routineId: 0001]----main i=3; prime=5, ch=0xc0000181e0, total=4
[routineId: 0005]----read i=7, in=0xc0000181e0, out=0xc00020a000
[routineId: 0001]----main i=4; prime=7, ch=0xc00020a000, total=5

4. 總結(jié)

  • 對(duì)Go不同協(xié)程中chan的傳遞原理了解不深,且素?cái)?shù)篩選代碼中多個(gè)協(xié)程統(tǒng)一使用了ch名稱,特別是對(duì)于main()中ch的重新賦值會(huì)不會(huì)影響其他協(xié)程不甚了解,導(dǎo)致理解混亂。
  • 經(jīng)深入分析代碼后理解了素?cái)?shù)篩選的內(nèi)部原理,可謂知其所以然,然如果讓自己來(lái)設(shè)計(jì),代碼肯定會(huì)臃腫非常多,對(duì)于大佬能用如此簡(jiǎn)單的代碼實(shí)現(xiàn)功能,萬(wàn)分欽佩!

新聞名稱:Go素?cái)?shù)篩選分析
URL分享:http://chinadenli.net/article40/dsoidho.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供小程序開(kāi)發(fā)網(wǎng)站設(shè)計(jì)公司、自適應(yīng)網(wǎng)站、用戶體驗(yàn)、網(wǎng)站設(shè)計(jì)、網(wǎng)站排名

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設(shè)