Go: 谨慎使用 math/rand 包中的默认随机数函数

栏目: Go · 发布时间: 7年前

内容简介:一句话总结:math/rand 包中默认的随机数相关函数共享了一个全局锁, 即:所有使用默认随机函数的代码都会去竞争一个全局锁,有时这可能不是你想要的结果。

一句话总结:math/rand 包中默认的随机数相关函数共享了一个全局锁, 即:所有使用默认随机函数的代码都会去竞争一个全局锁,有时这可能不是你想要的结果。

比如 rand.Int63 这个函数的 源代码 如下:

func Int63n(n int64) int64 { return globalRand.Int63n(n) }

可以看到它其实是调用了一个全局的 Rand 实例 globalRand ,我们来看一下 globalRand定义 :

var globalRand = New(&lockedSource{src: NewSource(1).(Source64)})

通过 New 的源码以及 globalRand.Int63n 的源码可以看到关键点是 lockedSource.Int63 方法的定义:

 type lockedSource struct {
     lk  sync.Mutex
     src Source64
 }

 func (r *lockedSource) Int63() (n int64) {
     r.lk.Lock()
     n = r.src.Int63()
     r.lk.Unlock()
     return
 }

通过同样的方法查看其他默认的随机函数可以发现,所有的默认随机函数都共享了一个全局锁,调用这些默认随机函数的时候都会先进行一次获取锁的操作。

大部分情况下不需要管这个全局锁的问题,因为大部分情况下都不会介意这点性能消耗。 如果确实特别在意这点性能消耗的话,可以通过定义一个你的包共享的或者结构体实例共享的 Rand 实例来优化锁的性能消耗(最小化锁的粒度,不跟其他包/代码竞争这个锁)。

例子:

type Xyz struct {
    // Rand 实例不是并发安全的,需要自行解决并发安全问题
    rndMu sync.Mutex
    rnd *rand.Rand
}

func (x *Xyz) random() int32 {
    x.rndMu.Lock()
    n := x.rnd.Int31()
    x.rndMu.Unlock()

    return n
}

func main() {
    x := &Xyz{
        rnd: rand.New(rand.NewSource(time.Now().UnixNano())),
    }
    fmt.Println(x.random())
}

或者可以考虑使用性能更好的第三方 rand 包: valyala/fastrand


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C专家编程

C专家编程

Peter Van Der Linden / 徐波 / 人民邮电出版社 / 2008-2 / 45.00元

《C专家编程》展示了最优秀的C程序员所使用的编码技巧,并专门开辟了一章对C++的基础知识进行了介绍。 书中C的历史、语言特性、声明、数组、指针、链接、运行时、内存以及如何进一步学习C++等问题进行了细致的讲解和深入的分析。全书撷取几十个实例进行讲解,对C程序员具有非常高的实用价值。 本书可以帮助有一定经验的C程序员成为C编程方面的专家,对于具备相当的C语言基础的程序员,本书可以帮助他们......一起来看看 《C专家编程》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具