最近在尝试用Golang写一个随机图API,但是关于随机数的生成产生了一些问题
原来的代码是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| func findImage(s string) []byte { imageList := make([]string, 0) err := filepath.Walk(s, func(p string, f os.FileInfo, err error) error { if f == nil { return nil } if f.IsDir() { return nil } ext := path.Ext(f.Name()) if ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif" { imageList = append(imageList, p) } return nil }) if err != nil { log.Fatal("in finding img ", err) } r := rand.New(rand.NewSource(time.Now().Unix())) i := r.Intn(len(imageList)) img := imageList[i] b, err := ioutil.ReadFile(img) if err != nil { log.Fatal("in opening img ", err) } return b }
|
我已经注意到需要用系统时间来作为生成随机数的种子,来避免随机序列不变的问题,但是事实上仍然出现了大量的重复数字,反映到程序里就是当快速刷新网页的时候,大概率连续显示的都是同一张图片
采用下列代码再次实验
1 2 3 4
| for i := 0; i < 5; i++ { r := rand.New(rand.NewSource(time.Now().Unix())) fmt.Println(r.Intn(100)) }
|
结果打印出来的都是同样的数字
在查阅了资料后发现,由于每次刷新网页该函数都会被调用一次,从而每次都新建了一个rand
的对象,但是计算机的处理速度很快,导致时间戳的分辨率不够,一秒内有多个对象被创建,从而它们的种子都是同一个,所以随机序列也是一样的,导致每次从序列取得第一个数字都是一样的
解决方案是将rand对象提升为全局变量,提前声明,这样对象只会被创建一次,大大避免了重复数字的产生
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| package randimg
import ( "io/ioutil" "log" "math/rand" "net/http" "os" "path" "path/filepath" "time"
"github.com/Unknwon/goconfig" )
var imageDirPath string var r = rand.New(rand.NewSource(time.Now().Unix()))
func GetRandomImage(w http.ResponseWriter, r *http.Request) { w.Write(findImage(imageDirPath)) }
func findImage(s string) []byte { imageList := make([]string, 0) err := filepath.Walk(s, func(p string, f os.FileInfo, err error) error { if f == nil { return nil } if f.IsDir() { return nil } ext := path.Ext(f.Name()) if ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif" { imageList = append(imageList, p) } return nil }) if err != nil { log.Fatal("in finding img ", err) } i := r.Intn(len(imageList)) img := imageList[i] b, err := ioutil.ReadFile(img) if err != nil { log.Fatal("in opening img ", err) } return b }
func init() { cfg, err := goconfig.LoadConfigFile("./config/randimg.ini") if err != nil { log.Fatal(err) } imageDirPath, err = cfg.GetValue(goconfig.DEFAULT_SECTION, "ImageDirPath") if err != nil { log.Fatal(err) } }
|