我正在编写一个应该有点快的数字程序,并且也是多线程的。我有一个表示数字的类,我想在其中使用随机数生成器。现在,我并不需要我的RNG成为真正的RNG,我只需要它生成0到NMAX之间均匀分布的整数。
所以我在课堂上有这个:
// use just an int here and forget about multithreading.
static uint32 rand = NMAX/4;
// this will be called multithreadedly
static uint32 GetRand() { return rand = ( rand + 1 ) % NMAX; }
现在,在单线程世界中,这对我的目的来说完全没问题。
由于这是多线程的,我假设唯一可能发生的坏事是偶尔(比如<1%的时间)更新被删除。意思是两个线程读取rand,在寄存器中更新它,返回更新的值,然后用相同的值写两次。这完全没问题。
我的问题是,还有什么比这更糟糕的事情?我对每个线程使用自己的rand
变量完全没问题,但这只是一个巨大的痛苦。我绝对不能做的是让它使每个类的实例使用自己的rand变量,因为它会占用过多的内存。
更新:
那么,为什么我要这样做呢?全文是一个使用1或2个字节的浮点类。所以它必须是快速的,这似乎是最好的方式。事实上,我认为我会将其从( rand + 1 ) % NMAX
更新为类似( rand + [some prime] ) % NMAX
的内容,因为它似乎效果更好。这是一个例子,其中一个更强大的解决方案需要更多的代码,使事情更少通用和更多的依赖性,使代码不那么清晰,更容易打破,所有的想法“应该使用适当的同步” 。
大多数情况下,我担心编译器可能会做一些奇怪的优化,因此rand的更新不仅会被删除,而且rand会变成完全垃圾。现在我考虑一下,即使也没关系(使用这个数字的方式),因为GetRand的下一次使用无论如何都是%NMAX,错误只会导致最多一次使用GetRand超出[0,NMAX]的给定范围。谢谢你的回答。
答案 0 :(得分:1)
我对每个帖子都很好 使用自己的
rand
变量,但那是 只是一个巨大的痛苦才能实现。
这样做并不一定非常困难。一些编译器(例如GCC)支持thread-local storage,它允许每个线程拥有自己的给定变量副本。
话虽如此,我能想到的只有一个问题 - 除了你提到的问题 - 用你目前的做法。如果每个线程在不同的核心上运行,并且随机值存储在每个核心的非共享缓存中,则更新可能无法在核心之间无限期地传播。您可以使用memory barrier(可以通过使用锁创建)来避免这种情况,但这可能对性能有害。
答案 1 :(得分:1)
为了讨论的目的,我们假设以下实现:
我的改进建议是每个实例一次获取16个数字。这些16个数字不必存储(复制)在实例中:您只需将全局索引递增16(使其对其他实例不可用),以便实例可以逐个使用它们。
答案 2 :(得分:1)
__declspec(thread) static uint32 rand = NMAX/4;
// this will be called multithreadedly
static uint32 GetRand() { return rand = ( rand + 1 ) % NMAX; }
static long rand = NMAX/4;
// this will be called multithreadedly
static uint32 GetRand() { return InterlockedIncrement(&rand) % NMAX; }
答案 3 :(得分:0)
如果两个线程同时调用GetRand,则可能发生经典的非同步错误。例如,rand = 10.在两个线程调用GetRand之后,rand应该是12,但实际上它可能是11.如果你没问题,你可以保留这些代码而不做任何更改。但我认为使用同步更好,因为没有它,代码及其结果看起来有点奇怪。另一个编程可以认为是bug。
编辑。
rand = ( rand + 1 ) % NMAX;
最坏情况:两个或多个线程从内存中读取相同的rand变量rand。每个线程在本地进行计算(rand + 1)%NMAX。然后所有线程将相同的结果返回到内存中。这不会破坏rand变量值,这不会导致此变量超出范围,数字生成器将继续计算有效数字。