亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

解決Go語言中高頻次和高并發(fā)下隨機(jī)數(shù)重復(fù)的問題

 更新時(shí)間:2023年12月05日 10:30:34   作者:Effend  
在Golang中,獲取隨機(jī)數(shù)的方法一般會(huì)介紹有兩種,一種是基于math/rand的偽隨機(jī),一種是基于crypto/rand的真隨機(jī),math/rand由于其偽隨機(jī)的原理,經(jīng)常會(huì)出現(xiàn)重復(fù)的隨機(jī)數(shù),導(dǎo)致在需要進(jìn)行隨機(jī)的業(yè)務(wù)出現(xiàn)較多的重復(fù)問題,所以本文給大家介紹了較好的解放方案

一、概要:

在Golang中,獲取隨機(jī)數(shù)的方法一般會(huì)介紹有兩種,一種是基于math/rand的偽隨機(jī),一種是基于crypto/rand的真隨機(jī)。其中,math/rand由于其偽隨機(jī)的原理,經(jīng)常會(huì)出現(xiàn)重復(fù)的隨機(jī)數(shù),導(dǎo)致在需要進(jìn)行隨機(jī)的業(yè)務(wù)出現(xiàn)較多的重復(fù)問題。在作者所經(jīng)歷的實(shí)際項(xiàng)目中便遇到在高頻率和高并發(fā)下獲取隨機(jī)數(shù)時(shí)的重復(fù)問題,在解決該問題的道路上,嘗試了幾種辦法,最終發(fā)現(xiàn)了較好的解決方案。

二、對(duì)math/rand隨機(jī)數(shù)的研究:

基于math/rand獲取隨機(jī)數(shù),常見的方法是:

import (
	"math/rand"
	"time"
)

r := rand.New(rand.NewSource(time.Now().UnixNano()))
result := r.Int31n(100)

上述代碼原理,是通過納秒時(shí)間來生成種子,然后通過該種子獲取一個(gè)隨機(jī)數(shù)。常用的開發(fā)者應(yīng)該知道,math/rand的種子具有其固定性,同一個(gè)數(shù)作為種子時(shí),通過New獲取的實(shí)例其實(shí)是相同的,即便它們的實(shí)例地址不同,但通過同樣的隨機(jī)數(shù)方法,獲得的值會(huì)是一樣的。

這種情況是由于math/rand會(huì)根據(jù)隨機(jī)種子進(jìn)行一個(gè)較復(fù)雜的算法過程,從而獲得一個(gè)長度為607的隨機(jī)數(shù)池,由于算法過程是固定的,不存在任何的隨機(jī)情況,因此同一個(gè)種子獲取的隨機(jī)數(shù)池是完全一致的。

此時(shí)可能會(huì)有人認(rèn)為,每次取不同的納秒時(shí)間來生成實(shí)例,不就可以解決問題?但這樣的想法在實(shí)際的項(xiàng)目實(shí)踐中是存在問題的。原因主要有兩點(diǎn):

1、Golang的一大優(yōu)點(diǎn)是速度快,當(dāng)一次性多次使用time.Now().UnixNano()獲取納秒時(shí)間時(shí),會(huì)發(fā)現(xiàn)納秒時(shí)間并不是不同的,而會(huì)存在重復(fù),這使得在高頻率隨機(jī)下隨機(jī)種子是一樣的。

2、Golang的一大特點(diǎn)是高并發(fā),但是在實(shí)際的運(yùn)行過程中,是無法保證兩個(gè)并發(fā)的協(xié)程不會(huì)在同一時(shí)間獲取時(shí)間的,同樣會(huì)出現(xiàn)重復(fù)問題。

針對(duì)這兩個(gè)問題也有對(duì)應(yīng)的解決方案:

1、對(duì)于第一個(gè)原因,可以在每次獲取納秒時(shí)間前,使用time.Sleep(time.Nanosecond)保證程序過一個(gè)時(shí)間再取,如此可以保證每次獲取的時(shí)間是不重復(fù)的。

2、對(duì)于第二個(gè)原因,可以利用鎖將獲取時(shí)間的過程鎖住,以保證并發(fā)時(shí)不在同一時(shí)間執(zhí)行。

然而在實(shí)際項(xiàng)目中實(shí)踐的情況并不理想。

第一個(gè)解決方案下,由于CPU處理的時(shí)間片,跳過的時(shí)間并不是1納秒,不同的CPU會(huì)有微小差異。作者在i7-13700K的CPU下測試,跳過前后的時(shí)間差大約為0.1ms。

第二個(gè)解決方案下,倘若不結(jié)合第一個(gè)解決方案,因?yàn)闊o法保證鎖前和鎖后的時(shí)間是否完全不一的情況,因此需要保留sleep過程,然而可以計(jì)算的是,若并發(fā)量達(dá)到了10000,僅是獲取種子的最長等待時(shí)間就可能達(dá)到1s,加上一般還會(huì)有其它業(yè)務(wù)邏輯,這難以保證高效的需求。

上述方案均存在隱含的問題,那么換一個(gè)解決思路,既然隨機(jī)數(shù)池是固定長度,不能使用sleep或鎖,為什么不讓程序只保留一個(gè)隨機(jī)實(shí)例并當(dāng)獲取次數(shù)達(dá)到一定值時(shí)更新隨機(jī)實(shí)例呢?我們使用下面這個(gè)簡單例子來測試可行性:

import (
	"math/rand"
	"time"
	"fmt"
)


t1 := time.Now().UnixNano()
fmt.Println(t1)
r := rand.New(rand.NewSource(t1))
for i := 0; i < 600; i++ {
	r.Int31n(100)
}
t2 := time.Now().UnixNano()
fmt.Println(t2)

上述代碼中,我們通過t1時(shí)間生成一個(gè)隨機(jī)實(shí)例,并讓該實(shí)例隨機(jī)600次,再獲取一次時(shí)間t2。執(zhí)行的結(jié)果會(huì)發(fā)現(xiàn)更大的問題,t1時(shí)間和t2時(shí)間是完全相同的!此時(shí)如果用t2作為種子,那么下一次隨機(jī)的600次會(huì)和t1時(shí)間的一模一樣。假如我們讓不同實(shí)例之間做延時(shí)或鎖,那么問題又會(huì)回到前面解決方案中同樣的問題。

因此,可以做出如下的結(jié)論。常用的math/rand在基于獲取當(dāng)前時(shí)間作為種子的隨機(jī)時(shí),無法真正地解決重復(fù)問題。

三、對(duì)crypto/rand隨機(jī)數(shù)的研究:

基于crypto/rand獲取隨機(jī)數(shù),常見的方法是:

import (
	"crypto/rand"
	"math/big"
)

r, _ := rand.Int(rand.Reader, big.NewInt(100))
result := r.Int64()

crypto/rand獲取的隨機(jī)數(shù)可以看做是真隨機(jī),其原因是它獲取種子的來源。簡單來說,在各類系統(tǒng)中,均存在一個(gè)特定的文件專門用于存儲(chǔ)真隨機(jī)值,這些值的來源是硬件在電路上產(chǎn)生的各種信息、熱噪聲等,由于它們是不可控的且無法預(yù)測的,因此可看做是產(chǎn)生了真正的隨機(jī)數(shù)。

在上述代碼中,rand.Reader就會(huì)從系統(tǒng)中存儲(chǔ)真隨機(jī)數(shù)的文件或者通過調(diào)用系統(tǒng)級(jí)別的API獲取一個(gè)真隨機(jī)數(shù)。對(duì)于系統(tǒng)而言,當(dāng)該隨機(jī)數(shù)被獲取后就會(huì)被銷毀,不會(huì)二次使用。

然而,crypto/rand有一個(gè)缺點(diǎn),它的獲取效率非常低,這是因?yàn)樗枰L問系統(tǒng)文件或者調(diào)用系統(tǒng)API,會(huì)產(chǎn)生不小的耗時(shí)。并且,每次從系統(tǒng)獲取的隨機(jī)值都會(huì)是一次性的,無法第二次使用。低效的獲取方式顯然并不能完全滿足高頻次和高并發(fā)下保持程序高效的需求。

四、最后的解決方案:

結(jié)合前面的相關(guān)內(nèi)容,可以找到一個(gè)取長補(bǔ)短的方式,得到一個(gè)解決方案:利用crypto/rand的rand.Reader獲取一個(gè)隨機(jī)數(shù),使用這個(gè)隨機(jī)數(shù)作為math/rand的種子獲得一個(gè)隨機(jī)數(shù)池進(jìn)行取值,當(dāng)取值達(dá)到一定次數(shù)后,再獲取一個(gè)新的種子生成新的實(shí)例。

通過rand.Reader獲取一個(gè)種子數(shù)的方法如下:

import (
	"crypto/rand"
	"encoding/binary"
)

var seed int64
binary.Read(rand.Reader, binary.BigEndian, &seed)

使用上述方法,即便高頻次或高并發(fā)地獲取種子數(shù),也能保證每次得到的種子都是不一樣的數(shù)值,這樣就能避免因種子相同導(dǎo)致的重復(fù)問題了。

最終的解決方案確定,結(jié)合后的代碼如下:

import (
	crand "crypto/rand"
	"encoding/binary"
	mrand "math/rand"
)

var seed int64
binary.Read(crand.Reader, binary.BigEndian, &seed)
source := mrand.NewSource(seed)
r := mrand.New(source)
result := r.Int31n(100)

在上述代碼中,仍然需要注意一個(gè)重要問題,就是crand.Reader的耗時(shí)較長,在對(duì)取隨機(jī)的過程進(jìn)行封裝時(shí),需要進(jìn)行進(jìn)一步的改進(jìn):

1、增加一個(gè)統(tǒng)計(jì)閾值,每次隨機(jī)后+1,并當(dāng)次數(shù)達(dá)到一定值時(shí)更新隨機(jī)實(shí)例;

2、對(duì)更新隨機(jī)實(shí)例過程上鎖,避免并發(fā)時(shí)出現(xiàn)重復(fù)更新;

經(jīng)過改進(jìn)后,可自行測試其速度。作者參與過的項(xiàng)目實(shí)踐中,在統(tǒng)計(jì)閾值為300且較高的獲取次數(shù)下,平均每次獲取一個(gè)隨機(jī)數(shù)的耗時(shí)約25~30ns。不僅滿足了接近真隨機(jī)的需求,并且滿足了高效的需求。

以上就是解決Go語言中高頻次和高并發(fā)下的隨機(jī)數(shù)重復(fù)的問題的詳細(xì)內(nèi)容,更多關(guān)于Go高頻次和高并發(fā)隨機(jī)數(shù)重復(fù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 一文帶大家搞懂Go語言中的迭代器

    一文帶大家搞懂Go語言中的迭代器

    迭代器是使用戶可在容器對(duì)象上遍訪的對(duì)象,設(shè)計(jì)人員使用此接口無需關(guān)心容器對(duì)象的內(nèi)存分配的實(shí)現(xiàn)細(xì)節(jié),本文主要為大家詳細(xì)介紹一下Go語言中的迭代器的實(shí)現(xiàn),需要的可以了解下
    2025-02-02
  • Go使用sync.Map來解決map的并發(fā)操作問題

    Go使用sync.Map來解決map的并發(fā)操作問題

    在 Golang 中 map 不是并發(fā)安全的,sync.Map 的引入確實(shí)解決了 map 的并發(fā)安全問題,本文就詳細(xì)的介紹一下如何使用,感興趣的可以了解一下
    2021-10-10
  • 使用gorm.Scopes函數(shù)實(shí)現(xiàn)復(fù)用查詢邏輯示例

    使用gorm.Scopes函數(shù)實(shí)現(xiàn)復(fù)用查詢邏輯示例

    這篇文章主要為大家介紹了使用gorm.Scopes函數(shù)實(shí)現(xiàn)復(fù)用查詢邏輯示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Go語言擴(kuò)展原語之Semaphore的用法詳解

    Go語言擴(kuò)展原語之Semaphore的用法詳解

    Go語言的擴(kuò)展包中提供了帶權(quán)重的信號(hào)量?semaphore.Weighted,讓我們可以按照不同的權(quán)重管理資源的訪問,下面小編就來和大家聊聊它們的具體用法吧
    2023-07-07
  • Go errors默認(rèn)加堆棧信息的作用分析

    Go errors默認(rèn)加堆棧信息的作用分析

    這篇文章主要為大家介紹了Go errors默認(rèn)加堆棧信息作用分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Go經(jīng)典面試題匯總(填空+判斷)

    Go經(jīng)典面試題匯總(填空+判斷)

    這篇文章主要介紹了Go經(jīng)典面試題匯總(填空+判斷),本文章內(nèi)容詳細(xì),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,需要的朋友可以參考下
    2023-01-01
  • Go語言包管理工具dep的安裝與使用

    Go語言包管理工具dep的安裝與使用

    godep是解決包依賴的管理工具,下面這篇文章主要給大家介紹了關(guān)于Go語言包管理工具dep的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • Go 數(shù)據(jù)結(jié)構(gòu)之堆排序示例詳解

    Go 數(shù)據(jù)結(jié)構(gòu)之堆排序示例詳解

    這篇文章主要為大家介紹了Go 數(shù)據(jù)結(jié)構(gòu)之堆排序示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Go語言Slice切片底層的實(shí)現(xiàn)

    Go語言Slice切片底層的實(shí)現(xiàn)

    本文主要介紹了Go語言Slice切片底層的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-04-04
  • go語言time.After()的作用

    go語言time.After()的作用

    time.After 是 Go 語言中的一個(gè)函數(shù),用于返回一個(gè)定時(shí)器通道,該通道在指定時(shí)間后發(fā)送當(dāng)前時(shí)間,這個(gè)功能常用于超時(shí)控制和延遲執(zhí)行,本文就來詳細(xì)的介紹一下,感興趣的可以了解學(xué)習(xí)
    2024-10-10

最新評(píng)論