关于随机数的问题

HarmondFu 2008-07-20 09:19:33
下面这个生成随机数的函数被多个线程同时调用,基本上都是正确的,但是偶尔还是有错误发生,实在不知道怎么回事:

char *get_rand(char *pRand, unsigned short len)
{
CString sFunc="get_rand";
unsigned short i;
char buf[3];
//DWORD j;

try
{
strcpy(pRand, "");
//throw( 0 );

//srand( 1 );
srand( GetTickCount() );
for(i=0; i<len; i++)
{
int nRand;

while((nRand = (rand()%16)) <= 0);


memset(buf, 0, sizeof(buf));
sprintf(buf, "%X", nRand);
strcat(pRand, buf);
}

}
catch(...)
{

}

return pRand;
}

其中传入的参数char *pRand是字符数组,不大可能是指针问题,由于很难促使问题重现,DrWatson又捕捉不到错误信息。有谁能看得出代码有什么问题吗?
...全文
196 33 打赏 收藏 转发到动态 举报
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
HarmondFu 2008-07-22
  • 打赏
  • 举报
回复
1. pRand确实是我怀疑的地方,当初设计时候没考虑这么周密,看来以后不能缺少验证这环节。

2. 随机种子是线程相关的吗?也就是说多线程同时调用srand(x),相互之间不会受干扰?楼上兄弟怎么得知的.


yinzhaohui 2008-07-22
  • 打赏
  • 举报
回复
while((nRand = (rand()%16)) <= 0); 可直接取绝对值
abs((rand()%16));
还有就是rand()是否为多线程安全的,你要在网上查一下
还有就是pRand的大小,确定不可能益出
Amuro1987218 2008-07-22
  • 打赏
  • 举报
回复
另外LZ是怎么知道pRand不太会出问题的?

线程调用上下文给个
Amuro1987218 2008-07-22
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 futinglian 的回复:]
现在还无法证实问题在哪里,有谁知道srand()与rand()之间,如果前者在多个线程内多次调用,是否会有负面影响?
[/Quote]

总之随机种子是线程相关的
HarmondFu 2008-07-22
  • 打赏
  • 举报
回复
现在还无法证实问题在哪里,有谁知道srand()与rand()之间,如果前者在多个线程内多次调用,是否会有负面影响?
Amuro1987218 2008-07-22
  • 打赏
  • 举报
回复
建个对话框程序把代码填进去运行就看到效果了,点OK后启动了2个线程,1个调用了srand(),1个没调用,然后各自都用rand()获得了5个随机数,并输出出来,是为了证明srand()是线程相关的.

_beginthread你通过这个函数启动线程就是线程安全的,他对CRT做了额外的处理最终还是调用WINAPI CreateThread()

CWinThread类的CreateThread()成员函数是通过_beginthreadex实现的.
HarmondFu 2008-07-22
  • 打赏
  • 举报
回复
非常感谢这么人的帮忙,好像问题变得复杂化了,我的问题越问越多:

1 明白srand的重要性和用途
2 好像没看懂Amuro1987218上面的代码,能否注释一下?
3 现在就是要在多线程中同时调用rand(),怎么通过beginthreadex保证线程安全?
我的线程是CWinThread派生类,也就是说,我是在CWinThread子类中产生随机数。


Amuro1987218 2008-07-22
  • 打赏
  • 举报
回复
void __cdecl srand (
unsigned int seed
)
{
#ifdef _MT

_getptd()->_holdrand = (unsigned long)seed;

#else /* _MT */
holdrand = (long)seed;
#endif /* _MT */
}

_ptiddata __cdecl _getptd (
void
)
{
_ptiddata ptd;
DWORD TL_LastError;


TL_LastError = GetLastError();
if ( (ptd = TlsGetValue(__tlsindex)) == NULL ) {
/*
* no per-thread data structure for this thread. try to create
* one.
*/
if ( ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) != NULL) &&
TlsSetValue(__tlsindex, (LPVOID)ptd) ) {

/*
* Initialize of per-thread data
*/

_initptd(ptd);

ptd->_tid = GetCurrentThreadId();
ptd->_thandle = (unsigned long)(-1L);
}
else
_amsg_exit(_RT_THREAD); /* write message and die */
}

SetLastError(TL_LastError);


return(ptd);
}

jxc25 2008-07-22
  • 打赏
  • 举报
回复
msvcrt!rand:
77c071d3 e84d2d0000 call msvcrt!_getptd (77c09f25)
77c071d8 8b4814 mov ecx,dword ptr [eax+14h]
77c071db 69c9fd430300 imul ecx,ecx,343FDh
77c071e1 81c1c39e2600 add ecx,269EC3h
77c071e7 894814 mov dword ptr [eax+14h],ecx
77c071ea 8bc1 mov eax,ecx
77c071ec c1e810 shr eax,10h
77c071ef 25ff7f0000 and eax,7FFFh
77c071f4 c3 ret
77c071f5 cc int 3
77c071f6 cc int 3
77c071f7 cc int 3
77c071f8 cc int 3
77c071f9 cc int 3
jwybobo2007 2008-07-22
  • 打赏
  • 举报
回复
没具体看代码,只提出注意点:

1.如果多线程使用全局变量,注意临界区保护.

2.rand为C运行时库中的函数,使用线程时应该使用_beginthreadex创建线程以保证线程安全
Amuro1987218 2008-07-22
  • 打赏
  • 举报
回复
把以前安的VC6找出来了,LZ请看:


#include <process.h>
VOID testRand(LPVOID flag)
{
int gaga[5];
if(flag)
{
srand(time(0));//注释掉本行看看

for(int i=0;i<5;++i)
gaga[i] = rand();
}
else
{
for(int i=0;i<5;++i)
gaga[i] = rand();
}
CString strResult;
strResult.Format("%d : %d %d %d %d %d",flag,gaga[0],gaga[1],gaga[2],gaga[3],gaga[4]);
AfxMessageBox(strResult);
}


void CDdDlg::OnOK()
{
// TODO: Add extra validation here
_beginthread(testRand,0,(LPVOID)0);//不"初始化"随机数种子
Sleep(1000*3);
_beginthread(testRand,0,(LPVOID)1);//"初始化"随机数种子

// CDialog::OnOK();
}
Amuro1987218 2008-07-22
  • 打赏
  • 举报
回复
[Quote=引用 26 楼 futinglian 的回复:]
1. pRand确实是我怀疑的地方,当初设计时候没考虑这么周密,看来以后不能缺少验证这环节。

2. 随机种子是线程相关的吗?也就是说多线程同时调用srand(x),相互之间不会受干扰?楼上兄弟怎么得知的.
[/Quote]


以前写的一个程序遇到过随机数方面的问题,这个LZ可以尝试下(我刚装系统,今天恐怕还装不了环境)

CRT负责维护随机种子,这是线程相关的,C语言的随机函数LZ应该知道是个有固定初试种子的数字产生器,不调用srand,在线程函数内连续调用rand并输出结果,你看看是不是一样.
jxc25 2008-07-21
  • 打赏
  • 举报
回复
char *get_rand(char *pRand, unsigned short len)
{return pRand;
}
改成void get_rand(char* pRand, unsigned short len)
{return;
}
可能是其它的类似警告中多了就成错误了
unsigned short len
unsigned short i;
char buf[3];
这些都是警告
jxc25 2008-07-21
  • 打赏
  • 举报
回复
srand( GetTickCount() );
要在init里用.
jxc25 2008-07-21
  • 打赏
  • 举报
回复
nRand=rand()%15+1;<<<while((nRand = (rand()%16)) <= 0);
HarmondFu 2008-07-21
  • 打赏
  • 举报
回复
楼上兄弟,能解释一下你这样做[nRand=rand()%15+1;]的原因吗?
jxc25 2008-07-21
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 jxc25 的回复:]
((nRand = (rand()%16)) <= 0); ??nRand <=0??
[/Quote]
nRand=rand()%15+1;
HarmondFu 2008-07-21
  • 打赏
  • 举报
回复
还有,如果我在try{}里面主动throw一个异常,尽管也会进入catch(){},但是不会导致服务停止。
HarmondFu 2008-07-21
  • 打赏
  • 举报
回复
原本是catch()后面代码打印出来的错误信息,我把它省略了。

楼上的兄弟,为什么说get_rand(strRand, 16)会出错?我差不多就是这样子调用的,而且第二个参数是动态计算的,我也考虑过有可能传入错误的缓冲区长度len,需要作判断,但因为是在生产环境,升级一次非常困难。

因为我认为这是可重入的函数,不需要加临界区保护,不过,catch(){}里面倒是用到了全局变量,是否会是这里破坏了函数的“可重入”性,程序是个Windows服务,每次get_rand出现异常整个服务就退出来了。

w_anthony 2008-07-21
  • 打赏
  • 举报
回复
LZ的代码如果使用多线程库的话,应该不会出错,是不是调用get_rand的时候参数填的有问题?
比如:
char strRand[16];
get_rand(strRand, 16);
这样就会出错了。

另外LZ是根据什么判断错误出在这里的?
加载更多回复(13)

16,470

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧