關於golang隨機種子的注意點

地球沒有花發表於2018-12-10

go語言裡,在併發下,設定隨機數種子的方法(Seed())和隨機數其他方法(比如,Intn())是不能共存的。

背景:之前在一個專案裡需要生成隨機數,上線之後總是報有大量的499超時,程式碼各種優化到沒得優化也找不出問題,哪知最後逐行排查打時間戳,居然是隨機數的原因,隨機數的產生居然需要10來ms。

當時的程式碼產生隨機數是這兩句:

rand.Seed(time.Now().UnixNano())
rnd_int := rand.Intn(11)

後來查原始碼文件有說明:// Seed should not be called concurrently with any other Rand method.

所以以後還是想起他辦法去隨機數吧,比如給時間戳取模之類的、用shuffle()方法洗牌再取之類。

最後,寫了個小程式,可以壓測一下看看效果:

package main

import (
        "fmt"
        "math/rand"
        "net/http"
        "strconv"
        "sync"
        "time"
)

type Demo struct {
        rnd  *rand.Rand
        Lock sync.RWMutex
}

func (d Demo) Get() int {
        d.Lock.RLock()
        defer d.Lock.RUnlock()
        d.rnd.Seed(time.Now().UnixNano())
        return d.rnd.Intn(11)
}
func main() {
        // 最快
        http.HandleFunc("/intn", func(w http.ResponseWriter, r *http.Request) {
                t1 := time.Now().UnixNano() / 1e3
                rnd_int := rand.Intn(11)
                t2 := time.Now().UnixNano() / 1e3
                fmt.Println(t2 - t1)
                rnd_str := strconv.Itoa(rnd_int)
                fmt.Fprintln(w, rnd_str, r.URL.Path)
        })
        // 不能併發取,會慢
        http.HandleFunc("/seed", func(w http.ResponseWriter, r *http.Request) {
                t1 := time.Now().UnixNano() / 1e3
                rand.Seed(time.Now().UnixNano())
                rnd_int := rand.Intn(10-0+1) + 0
                t2 := time.Now().UnixNano() / 1e3
                fmt.Println(t2 - t1)
                rnd_str := strconv.Itoa(rnd_int)
                fmt.Fprintln(w, rnd_str, r.URL.Path)
        })
        // 可以併發取,加了鎖
        http.HandleFunc("/seedCon", func(w http.ResponseWriter, r *http.Request) {
                t1 := time.Now().UnixNano() / 1e3
                rObj := Demo{
                        rnd : rand.New(rand.NewSource(time.Now().UnixNano())),
                }
                rnd_int := rObj.Get()
                t2 := time.Now().UnixNano() / 1e3
                fmt.Println(t2 - t1)
                rnd_str := strconv.Itoa(rnd_int)
                fmt.Fprintln(w, rnd_str, r.URL.Path)
        })
        //監聽3000埠
        http.ListenAndServe(":3000", nil)
}
wrk -t200 -c200 -d60s "http://127.0.0.1:3000/intn"
wrk -t200 -c200 -d60s "http://127.0.0.1:3000/seed"
wrk -t200 -c200 -d60s "http://127.0.0.1:3000/seedCon"

 

相關文章