为啥分叉后 rand() 不那么随机?
Posted
技术标签:
【中文标题】为啥分叉后 rand() 不那么随机?【英文标题】:Why is rand() not so random after fork?为什么分叉后 rand() 不那么随机? 【发布时间】:2012-01-27 05:32:56 【问题描述】:#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
int i =10;
/* initialize random seed: */
srand(time(NULL));
while(i--)
if(fork()==0)
/* initialize random seed here does not make a difference:
srand(time(NULL));
*/
printf("%d : %d\n",i,rand());
return;
return (EXIT_SUCCESS);
打印相同(每次运行不同)数字 10 次 - 预期? 我有一段更复杂的代码,每个分叉的进程依次运行 - 没有区别
【问题讨论】:
非常感谢您的回答 - 现在清楚了 :) 【参考方案1】:这样就解决了问题:
srand48((long int)time(NULL));
i= (lrand48()/rand()+1) % 123
我没有用 fork 测试过,但是在 for 调用 100 次里面它可以工作。
带有 pid 号的种子。解决起来有点困难。
这是在某个页面中:“这有效 srand(time(0)+getpid()); 但我必须在案例 0 中调用它,即子进程”。
【讨论】:
我听不懂你的英文【参考方案2】:输出必须相同。如果两个进程每个都用相同的种子播种随机数并且每个调用一次rand
,它们必须得到相同的结果。这就是拥有种子的全部意义所在。您的所有进程都使用相同的种子调用srand
(因为您只调用一次srand
)并且它们都调用一次rand
,因此它们必须得到相同的结果。
取消注释 srand
不会产生影响,因为除非秒数发生变化,否则它们仍会给出相同的种子。你可以这样做:
srand(time(NULL) ^ (getpid()<<16));
【讨论】:
您介意简要解释一下^ (getpid()<<16)
的幕后情况吗?
time(NULL)
确保您每秒获得不同的值。 (getpid()<<16)
增加了您为每个进程获得不同值的几率,因为进程 ID 通常不会那么快地重用。
我得到了那部分先生,我的意思是为什么使用 XOR 运算而不是 & 或 |?我在那个(数学)部分。
@snr XOR 确保任何一方的任何更改都会更改结果。 8&1 与 8&2 相同,所以 & 被淘汰。 9|1 与 9|9 相同,所以 |出去了。【参考方案3】:
即使在循环中添加srand(time(NULL));
(您已评论的if
块内的行)也没有什么不同,因为现代计算机可以非常快速地执行整个块,并且time
以秒计。从手册页:
time() 以自 Epoch 以来的秒数返回时间...
如果您在while
循环中的if
语句之后添加sleep(1);
并取消注释srand
调用,结果将会不同,因为time
现在将返回不同的值,因为第二个有已过。
然而,使用不同的种子值比等待更合适。像i
这样的东西会是个好主意,因为它对于循环的每次迭代都是唯一的。
【讨论】:
【参考方案4】:当你创建一个子进程时,你并没有重新播种。随机数发生器的状态完全一样。
即使您再次在孩子身上播种,您也会以 +/- 1 秒的粒度播种时间。当你分叉时,这一切都会在不到一秒钟的时间内发生。
尝试用不同且更随机的东西来播种它。
【讨论】:
【参考方案5】:如果您的代码运行得足够快,srand()
可能会在每个分叉的时间完全相同。 time()
每秒都在变化。
【讨论】:
【参考方案6】:这是因为所有程序都使用相同的值播种(在该 while 循环之外)。一旦你分叉了新程序,你应该再次播种,否则两者都会产生相同的序列。
【讨论】:
【参考方案7】:rand()
函数是一个伪随机数字生成器。这意味着生成的数字序列是确定性的,仅取决于提供的种子。
因为您将同一个进程分叉 10 次,所以随机数生成器的状态对于每个子进程都是相同的。下次您调用rand()
时,您将获得相同的值。
通过在子进程中调用srand(time(NULL))
,您可能提供帮助,但time()
的粒度仅为1 秒,因此您的所有子进程可能在同一秒内开始。使用相同的值播种会生成相同的伪随机序列。
您可以尝试使用取决于子编号的值进行播种:
srand(time(NULL) - i*2);
(如果 time()
在 fork 循环期间提前 1 秒,我使用了 i*2
。)
【讨论】:
总体来说是个好主意,但明智的做法是以更重要的方式合并子编号。time(NULL) + 100 * i
或类似的东西,以确保一两秒的差异不会影响种子。
好点,我喜欢 David Schwartz 使用子 pid 的想法。
是的,我真的很喜欢他在 high 中混合它的方式,但是对于使用 clone(2)
而不是 fork(2)
的应用程序,它们都可以pid
具有相同的值,但 i
的值不同。可能对任何人都无关紧要的细微差别......
pid 的好处是即使运行速度非常快,结果也不会相同以上是关于为啥分叉后 rand() 不那么随机?的主要内容,如果未能解决你的问题,请参考以下文章