亚洲综合原千岁中文字幕_国产精品99久久久久久久vr_无码人妻aⅴ一区二区三区浪潮_成人h动漫精品一区二区三

主頁 > 知識庫 > 一文完全掌握 Go math/rand(源碼解析)

一文完全掌握 Go math/rand(源碼解析)

熱門標簽:百度地圖標注后傳給手機 壽光微信地圖標注 外呼系統用什么卡 電話機器人軟件免費 外呼系統顯本地手機號 涿州代理外呼系統 阿克蘇地圖標注 評價高的400電話辦理 excel地圖標注分布數據

Go 獲取隨機數是開發中經常會用到的功能, 不過這個里面還是有一些坑存在的, 本文將完全剖析 Go math/rand, 讓你輕松使用 Go Rand.

開篇一問: 你覺得 rand 會 panic 嗎 ?

源碼剖析

math/rand 源碼其實很簡單, 就兩個比較重要的函數

func (rng *rngSource) Seed(seed int64) {
	rng.tap = 0
	rng.feed = rngLen - rngTap

	//...
	x := int32(seed)
	for i := -20; i  rngLen; i++ {
		x = seedrand(x)
		if i >= 0 {
			var u int64
			u = int64(x)  40
			x = seedrand(x)
			u ^= int64(x)  20
			x = seedrand(x)
			u ^= int64(x)
			u ^= rngCooked[i]
			rng.vec[i] = u
		}
	}
}

這個函數就是在設置 seed, 其實就是對 rng.vec 各個位置設置對應的值. rng.vec 的大小是 607.

func (rng *rngSource) Uint64() uint64 {
	rng.tap--
	if rng.tap  0 {
		rng.tap += rngLen
	}

	rng.feed--
	if rng.feed  0 {
		rng.feed += rngLen
	}

	x := rng.vec[rng.feed] + rng.vec[rng.tap]
	rng.vec[rng.feed] = x
	return uint64(x)
}

我們在使用不管調用 Intn(), Int31n() 等其他函數, 最終調用到就是這個函數. 可以看到每次調用就是利用 rng.feed rng.tap 從 rng.vec 中取到兩個值相加的結果返回了. 同時還是這個結果又重新放入 rng.vec.

在這里需要注意使用 rng.go 的 rngSource 時, 由于 rng.vec 在獲取隨機數時會同時設置 rng.vec 的值, 當多 goroutine 同時調用時就會有數據競爭問題. math/rand 采用在調用 rngSource 時加鎖 sync.Mutex 解決.

func (r *lockedSource) Uint64() (n uint64) {
	r.lk.Lock()
	n = r.src.Uint64()
	r.lk.Unlock()
	return
}

另外我們能直接使用 rand.Seed(), rand.Intn(100), 是因為 math/rand 初始化了一個全局的 globalRand 變量.

var globalRand = New(lockedSource{src: NewSource(1).(*rngSource)})

func Seed(seed int64) { globalRand.Seed(seed) }

func Uint32() uint32 { return globalRand.Uint32() }

需要注意到由于調用 rngSource 加了鎖, 所以直接使用 rand.Int32() 會導致全局的 goroutine 鎖競爭, 所以在高并發場景時, 當你的程序的性能是卡在這里的話, 你需要考慮利用 New(lockedSource{src: NewSource(1).(*rngSource)}) 為不同的模塊生成單獨的 rand. 不過根據目前的實踐來看, 使用全局的 globalRand 鎖競爭并沒有我們想象中那么激烈. 使用 New 生成新的 rand 里面是有坑的, 開篇的 panic 就是這么產生的, 后面具體再說.

種子(seed)到底起什么作用 ?

func main() {
	for i := 0; i  10; i++ {
		fmt.Printf("current:%d\n", time.Now().Unix())
		rand.Seed(time.Now().Unix())
		fmt.Println(rand.Intn(100))
	}
}

結果:

current:1613814632
65
current:1613814632
65
current:1613814632
65
...

這個例子能得出一個結論: 相同種子,每次運行的結果都是一樣的. 這是為什么呢?

在使用 math/rand 的時候, 一定需要通過調用 rand.Seed 來設置種子, 其實就是給 rng.vec 的 607 個槽設置對應的值. 通過上面的源碼那可以看出來, rand.Seed 會調用一個 seedrand 的函數, 來計算對應槽的值.

func seedrand(x int32) int32 {
	const (
		A = 48271
		Q = 44488
		R = 3399
	)

	hi := x / Q
	lo := x % Q
	x = A*lo - R*hi
	if x  0 {
		x += int32max
	}
	return x
}

這個函數的計算結果并不是隨機的, 而是根據 seed 實際算出來的. 另外這個函數并不是隨便寫的, 是有相關的數學證明的.

這也導致了相同的 seed, 最終設置到 rng.vec里面的值是相同的, 通過 Intn 取出的也是相同的值

我遇到的那些坑

1. rand panic

文章開頭的截圖就是項目開發中使用別人封裝的底層庫, 在某天出現的 panic. 大概實現的代碼

// random.go

var (
	rrRand = rand.New(rand.NewSource(time.Now().Unix()))
)

type Random struct{}

func (r *Random) Balance(sf *service.Service) ([]string, error) {
	// .. 通過服務發現獲取到一堆ip+port, 然后隨機拿到其中的一些ip和port出來
	randIndexes := rrRand.Perm(randMax)

	// 返回這些ip 和port
}

這個 Random 會被并發調用, 由于 rrRand 不是并發安全的, 所以就導致了調用 rrRand.Perm 時偶爾會出現 panic 情況.

在使用 math/rand 的時候, 有些人使用 math.Intn() 看了下注釋發現是全局共享了一個鎖, 擔心出現鎖競爭, 所以用 rand.New 來初始化一個新的 rand, 但是要注意到 rand.New 初始化出來的 rand 并不是并發安全的.

修復方案: 就是把 rrRand 換成了 globalRand, 在線上高并發場景下, 發現全局鎖影響并不大.

2. 獲取的都是同一個機器

同樣也是底層封裝的 rpc 庫, 使用 random 的方式來流量分發, 在線上跑了一段時間后, 流量都路由到一臺機器上了, 導致服務直接宕機. 大概實現代碼:

func Call(ctx *gin.Context, method string, service string, data map[string]interface{}) (buf []byte, err error) {
	ins, err := ral.GetInstance(ctx, ral.TYPE_HTTP, service)
	if err != nil {
		// 錯誤處理
	}
	defer ins.Release()

	if b, e := ins.Request(ctx, method, data, head); e == nil {
		// 錯誤處理
	}
	// 其他邏輯, 重試等等
}

func GetInstance(ctx *gin.Context, modType string, name string) (*Instance, error) {
	// 其他邏輯..

	switch res.Strategy {
	case WITH_RANDOM:
		if res.rand == nil {
			res.rand = rand.New(rand.NewSource(time.Now().Unix()))
		}
		which = res.rand.Intn(res.count)
	case 其他負載均衡查了
	}

	// 返回其中一個ip和port
}

引起問題的原因: 可以看出來每次請求到來都是利用 GetInstance 來獲取一個 ip 和 port, 如果采用 Random 方式的流量負載均衡, 每次都是重新初始化一個 rand. 我們已經知道當設置相同的種子,每次運行的結果都是一樣的. 當瞬間流量過大時, 并發請求 GetInstance, 由于那一刻 time.Now().Unix() 的值是一樣的, 這樣就會導致獲取到隨機數都是一樣的, 所以就導致最后獲取到的 ip, port都是一樣的, 流量都分發到這臺機器上了.

修復方案: 修改成 globalRand 即可.

rand 未來期望

說到這里基本上可以看出來, 為了防止全局鎖競爭問題, 在使用 math/rand 的時候, 首先都會想到自定義 rand, 但是就容易整出來莫名其妙的問題.

為什么 math/rand 需要加鎖呢?

大家都知道 math/rand 是偽隨機的, 但是在設置完 seed 后, rng.vec 數組的值基本上就確定下來了, 這明顯就不是隨機了, 為了增加隨機性, 通過 Uint64() 獲取到隨機數后, 還會重新去設置 rng.vec. 由于存在并發獲取隨機數的需求, 也就有了并發設置 rng.vec 的值, 所以需要對 rng.vec 加鎖保護.

使用 rand.Intn() 確實會有全局鎖競爭問題, 你覺得 math/rand 未來會優化嗎? 以及如何優化? 歡迎留言討論

到此這篇關于一文完全掌握 Go math/rand(源碼解析)的文章就介紹到這了,更多相關Go math rand內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • 利用Vue.js+Node.js+MongoDB實現一個博客系統(附源碼)
  • python django事務transaction源碼分析詳解
  • avalon js實現仿google plus圖片多張拖動排序附源碼下載
  • 可以查詢google排名的asp源碼

標簽:吐魯番 蘭州 雞西 梅河口 欽州 汕頭 重慶 銅川

巨人網絡通訊聲明:本文標題《一文完全掌握 Go math/rand(源碼解析)》,本文關鍵詞  一文,完全,掌握,math,rand,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《一文完全掌握 Go math/rand(源碼解析)》相關的同類信息!
  • 本頁收集關于一文完全掌握 Go math/rand(源碼解析)的相關信息資訊供網民參考!
  • 推薦文章
    精品久久久久久影院免费| 青青久久精品国产免费看| 黄色免费三级| 你懂的在线观看视频| 亚州视频一区二区| 一级毛片视频在线观看| 天天做日日爱夜夜爽| 精品在线观看国产| 日韩中文字幕一区| 日日爽天天| 欧美国产日韩精品| 色综合久久久久综合体桃花网| 一级女性大黄生活片免费| 九九精品久久久久久久久| 亚飞与亚基在线观看| 亚洲精品久久久中文字| 一级女性大黄生活片免费| 一级片免费在线观看视频| 毛片的网站| 国产亚洲免费观看| 二级特黄绝大片免费视频大片| 高清一级淫片a级中文字幕 | 在线观看导航| 国产原创中文字幕| 国产伦精品一区二区三区在线观看 | 久草免费在线视频| 韩国毛片免费| 精品在线观看一区| 久久久久久久久综合影视网| 国产伦精品一区二区三区无广告| 国产原创视频在线| 亚洲爆爽| 韩国毛片 免费| a级毛片免费观看网站| 国产美女在线观看| 日韩中文字幕在线观看视频| 青青久热| 韩国三级一区| 日本免费区| 黄色福利片| 欧美另类videosbestsex久久| 欧美1卡一卡二卡三新区| 欧美18性精品| 一级女性全黄生活片免费 | 人人干人人插| 精品国产三级a| 色综合久久久久综合体桃花网| 久久成人性色生活片| 99久久网站| 欧美国产日韩精品| 午夜在线影院| 欧美a级片视频| 欧美另类videosbestsex高清| 香蕉视频久久| 精品视频免费看| 国产原创视频在线| 一级女性全黄生活片免费| 久久久成人网| 四虎影视久久久免费| 韩国毛片免费| 日韩一级精品视频在线观看| 国产亚洲免费观看| 日韩一级黄色| 日韩女人做爰大片| 九九国产| 欧美激情在线精品video| 国产原创视频在线| 99久久网站| 可以免费看毛片的网站| 国产原创视频在线| 日韩专区第一页| 成人免费观看网欧美片| 国产网站在线| 亚洲第一色在线| 日本久久久久久久 97久久精品一区二区三区 狠狠色噜噜狠狠狠狠97 日日干综合 五月天婷婷在线观看高清 九色福利视频 | 国产麻豆精品视频| 四虎影视久久久| 国产麻豆精品高清在线播放| 亚欧成人乱码一区二区| 美女免费精品视频在线观看| 色综合久久天天综合绕观看| 久久国产精品只做精品| 国产国语对白一级毛片| 黄视频网站免费| 尤物视频网站在线| 国产激情视频在线观看| 可以免费看污视频的网站| 国产视频一区在线| 99色视频在线观看| 亚洲不卡一区二区三区在线| 国产成人精品综合在线| 欧美激情一区二区三区视频 | 国产网站麻豆精品视频| 99久久精品国产高清一区二区| 午夜在线亚洲男人午在线| 国产精品自拍亚洲| 青青久久精品国产免费看| 成人高清视频免费观看| 国产美女在线观看| 午夜欧美成人久久久久久| 欧美激情一区二区三区视频高清| 日本久久久久久久 97久久精品一区二区三区 狠狠色噜噜狠狠狠狠97 日日干综合 五月天婷婷在线观看高清 九色福利视频 | 可以在线看黄的网站| 欧美激情伊人| 精品视频免费在线| 一级女性全黄生活片免费| 亚洲第一页乱| 韩国三级视频在线观看| 国产一区二区高清视频| 国产美女在线一区二区三区| 在线观看成人网| 九九久久国产精品| 欧美夜夜骑 青草视频在线观看完整版 久久精品99无色码中文字幕 欧美日韩一区二区在线观看视频 欧美中文字幕在线视频 www.99精品 香蕉视频久久 | 精品久久久久久免费影院| 你懂的日韩| 亚洲天堂在线播放| 精品久久久久久中文| 天天色成人| 精品久久久久久中文字幕一区| 久草免费资源| 亚洲天堂免费| 国产网站麻豆精品视频| 欧美a级片免费看| 欧美国产日韩在线| 精品国产一区二区三区国产馆| 国产精品1024在线永久免费 | 精品国产一级毛片| 国产91素人搭讪系列天堂| 精品视频在线观看免费 | 国产精品免费久久| 毛片高清| 欧美激情一区二区三区视频| 国产成人精品综合久久久| 久久99中文字幕久久| 九九精品影院| 99久久精品国产片| 日韩专区亚洲综合久久| 好男人天堂网 久久精品国产这里是免费 国产精品成人一区二区 男人天堂网2021 男人的天堂在线观看 丁香六月综合激情 | 台湾美女古装一级毛片| 沈樵在线观看福利| 天天做人人爱夜夜爽2020| 精品久久久久久中文字幕2017| 91麻豆精品国产综合久久久| 久久精品免视看国产明星| 亚洲女人国产香蕉久久精品| 精品国产一区二区三区免费| 韩国三级香港三级日本三级| 精品在线视频播放| 成人免费观看网欧美片| 国产国语在线播放视频| 成人在激情在线视频| 天天色成人网| 国产国语在线播放视频| 日韩中文字幕在线亚洲一区| 午夜在线亚洲男人午在线| 99热精品在线| 久久99爰这里有精品国产| 日本伦理黄色大片在线观看网站| 国产伦精品一区二区三区无广告| 精品国产一区二区三区久久久蜜臀| 韩国三级一区| 国产一区免费在线观看| 成人在免费观看视频国产| 国产成人精品一区二区视频| 沈樵在线观看福利| 国产一区二区高清视频| 台湾美女古装一级毛片| 欧美激情一区二区三区中文字幕| 99色吧| 中文字幕一区二区三区 精品| 欧美一区二区三区性| 日韩在线观看免费完整版视频| 欧美激情在线精品video| 可以在线看黄的网站| 久久精品店| 欧美另类videosbestsex高清| 国产亚洲精品成人a在线| 国产一区免费在线观看| 欧美1区| 香蕉视频久久| 999久久狠狠免费精品| 国产精品1024在线永久免费 | 色综合久久手机在线| 国产亚洲精品aaa大片| 国产激情一区二区三区| 国产成a人片在线观看视频| 国产网站免费在线观看| 中文字幕97| 欧美a级片视频| 精品国产亚洲人成在线| 青青久久精品国产免费看| 亚久久伊人精品青青草原2020| 美国一区二区三区| 久久久久久久男人的天堂| 成人免费网站视频ww| 欧美夜夜骑 青草视频在线观看完整版 久久精品99无色码中文字幕 欧美日韩一区二区在线观看视频 欧美中文字幕在线视频 www.99精品 香蕉视频久久 | 国产一级生活片| 国产91素人搭讪系列天堂| 久久99这里只有精品国产| 精品国产一区二区三区国产馆|