最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(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ù)問(wèn)題,所以本文給大家介紹了較好的解放方案

一、概要:

在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ù)問(wèn)題。在作者所經(jīng)歷的實(shí)際項(xiàng)目中便遇到在高頻率和高并發(fā)下獲取隨機(jī)數(shù)時(shí)的重復(fù)問(wèn)題,在解決該問(wèn)題的道路上,嘗試了幾種辦法,最終發(fā)現(xiàn)了較好的解決方案。

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

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

import (
	"math/rand"
	"time"
)

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

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

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

此時(shí)可能會(huì)有人認(rèn)為,每次取不同的納秒時(shí)間來(lái)生成實(shí)例,不就可以解決問(wèn)題?但這樣的想法在實(shí)際的項(xiàng)目實(shí)踐中是存在問(wèn)題的。原因主要有兩點(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)行過(guò)程中,是無(wú)法保證兩個(gè)并發(fā)的協(xié)程不會(huì)在同一時(shí)間獲取時(shí)間的,同樣會(huì)出現(xiàn)重復(fù)問(wèn)題。

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

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

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

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

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

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

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

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)

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

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

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

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

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

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

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

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

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

四、最后的解決方案:

結(jié)合前面的相關(guān)內(nèi)容,可以找到一個(gè)取長(zhǎng)補(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í)例。

通過(guò)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ù)問(wèn)題了。

最終的解決方案確定,結(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è)重要問(wèn)題,就是crand.Reader的耗時(shí)較長(zhǎng),在對(duì)取隨機(jī)的過(guò)程進(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í)例過(guò)程上鎖,避免并發(fā)時(shí)出現(xiàn)重復(fù)更新;

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

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

相關(guān)文章

  • golang1.21泛型函數(shù)全面講解

    golang1.21泛型函數(shù)全面講解

    在Go編程語(yǔ)言中,泛型一直是一個(gè)備受期待的特性,隨著Go?1.21的發(fā)布,本文旨在提供Go?1.21中泛型的詳細(xì)探索,闡明它們的優(yōu)點(diǎn)、語(yǔ)法、實(shí)現(xiàn)和最佳實(shí)踐,希望對(duì)大家有所幫助
    2023-09-09
  • Go操作Kafka和Etcd方法詳解

    Go操作Kafka和Etcd方法詳解

    這篇文章主要為大家介紹了Go操作Kafka和Etcd方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • Golang 類(lèi)型斷言的具體使用

    Golang 類(lèi)型斷言的具體使用

    本文主要介紹了Golang 類(lèi)型斷言的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • golang打包成帶圖標(biāo)的exe可執(zhí)行文件

    golang打包成帶圖標(biāo)的exe可執(zhí)行文件

    這篇文章主要給大家介紹了關(guān)于golang打包成帶圖標(biāo)的exe可執(zhí)行文件的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-06-06
  • Golang中零切片、空切片、nil切片

    Golang中零切片、空切片、nil切片

    切片是最常用的數(shù)據(jù)結(jié)構(gòu)之一,但許多開(kāi)發(fā)者對(duì)零切片、空切片和nil切片的概念模糊不清,本文主要介紹了這三種的區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-06-06
  • Go type關(guān)鍵字(類(lèi)型定義與類(lèi)型別名的使用差異)用法實(shí)例探究

    Go type關(guān)鍵字(類(lèi)型定義與類(lèi)型別名的使用差異)用法實(shí)例探究

    這篇文章主要為大家介紹了Go type關(guān)鍵字(類(lèi)型定義與類(lèi)型別名的使用差異)用法實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Go語(yǔ)言中的自定義類(lèi)型你了解嗎

    Go語(yǔ)言中的自定義類(lèi)型你了解嗎

    自定義類(lèi)型是 Go 語(yǔ)言中非常重要的概念之一,通過(guò)自定義類(lèi)型,我們可以更好地封裝數(shù)據(jù)、組織代碼,提高程序的可讀性和可維護(hù)性。本文將從以下幾個(gè)方面介紹 Go 自定義類(lèi)型的相關(guān)知識(shí),感興趣的可以了解一下
    2023-04-04
  • Go語(yǔ)句與表達(dá)式案例手冊(cè)深度解析

    Go語(yǔ)句與表達(dá)式案例手冊(cè)深度解析

    這篇文章主要為大家介紹了Go語(yǔ)句與表達(dá)式案例手冊(cè)深度解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Golang中的自定義函數(shù)類(lèi)型詳解

    Golang中的自定義函數(shù)類(lèi)型詳解

    在 Golang 中,type 關(guān)鍵字用于定義自定義類(lèi)型,函數(shù)也是一種數(shù)據(jù)類(lèi)型,因此可以使用 type 關(guān)鍵字來(lái)定義函數(shù)類(lèi)型,本文就給大家詳細(xì)介紹一下Golang中的自定義函數(shù)類(lèi)型,需要的朋友可以參考下
    2023-07-07
  • Go時(shí)間格式化的實(shí)現(xiàn)

    Go時(shí)間格式化的實(shí)現(xiàn)

    本文主要介紹了Go時(shí)間格式化的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06

最新評(píng)論

博爱县| 曲松县| 孝昌县| 离岛区| 赣州市| 达孜县| 岳普湖县| 公安县| 稷山县| 吉林市| 仁怀市| 富平县| 麟游县| 尉犁县| 武乡县| 武胜县| 景德镇市| 达拉特旗| 名山县| 洛浦县| 萍乡市| 科尔| 秦安县| 青田县| 马龙县| 宣汉县| 德钦县| 纳雍县| 化州市| 镇坪县| 丰顺县| 蒲江县| 张家川| 霸州市| 通海县| 尉氏县| 西乌珠穆沁旗| 莱芜市| 青铜峡市| 南雄市| 连城县|