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"