程序在启动后,初始化的时候正确调用了srand(time(NULL)). 但是在之后使用的地方,rand() 得到的随机数序列,每次程序运行都是一致的。也就是srand()根本没起作用。更进一步调试,把srand注释掉,得到的随机数序列是一致的。这就奇怪了。
调了半天,没啥进展,跟进srand的实现看看:
最后自己写段程序确认下这个问题:
main: 6334 18467 41
main: 23075 19053 440
thread: 6334 18467 41
thread: 23075 19053 440
与预期相符。第一行,第三行分别是主函数与线程函数中输出的随机数。这时主线程与子线程均未调用srand,按默认的输出,结果一样。
第二行,第四行,主线程与子线程均已设置srand(123),则输出新的序列。
调了半天,没啥进展,跟进srand的实现看看:
void __cdecl srand ( unsigned int seed )
{
_getptd()->_holdrand = (unsigned long)seed;
}
这部分代码很简单,直接设置一下。问题是这个_getptd()是个什么东西。原来是个包装函数:
_ptiddata __cdecl _getptd ( void )
{
_ptiddata ptd = _getptd_noexit();
if (!ptd) {
_amsg_exit(_RT_THREAD); /* write message and die */
}
return ptd;
}
继续跟_getptd_noexit(),在这个函数里面,真相大白了。原来这个是获得当前thread相关的data指针:
/** _ptiddata _getptd(void) - get per-thread data structure for the current thread
换句话说,srand的时候,仅仅是把种子设置到当前线程的数据块中。这是线程相关的(就像一个ThreadLocal)。如果之后调用rand()是在另外一个线程中,那么还是默认的随机数种子。这就是这个bug的原因:srand() 和 rand() 发生在不同的线程中。
最后确认一下rand()函数:int __cdecl rand ( void )
{
_ptiddata ptd = _getptd();
return( ((ptd->_holdrand = ptd->_holdrand * 214013L
+ 2531011L) >> 16) & 0x7fff );
}
果然是从当前线程的数据中获得种子,然后算出一个伪随机数。
最后自己写段程序确认下这个问题:
void run(void *) {
printf("thread: %d %d %d\n", rand(), rand(), rand());
srand(123);
printf("thread: %d %d %d\n", rand(), rand(), rand());
}
int main(int argc, char* argv[]) {
printf("main: %d %d %d\n", rand(), rand(), rand());
srand(123);
printf("main: %d %d %d\n", rand(), rand(), rand());
_beginthread(run, 0, NULL);
getchar();
}
运行结果:
main: 6334 18467 41
main: 23075 19053 440
thread: 6334 18467 41
thread: 23075 19053 440
与预期相符。第一行,第三行分别是主函数与线程函数中输出的随机数。这时主线程与子线程均未调用srand,按默认的输出,结果一样。
第二行,第四行,主线程与子线程均已设置srand(123),则输出新的序列。