android 多线程 随机数,多线程安全的随机数生产函数

(一)遇到的问题

软件中写了这么一个函数,用来生成有数字和字母组成的随机的指定字符个数的字符串。运行的时候发现多线程调用这个函数,返回的两个字符串一模一样。这是不符合预期的,会导致程序出现bug.

原因很简单,多个线程如果同时调用到这个函数,那么time(NULL)返回的种子是一模一样的,所以rand函数返回的序列在两个线程中也是一模一样的。所有返回的字符串也是一模一样的。

这个是伪随机数的性质:相同的种子返回相同的随机数序列。

string GenerateRandomString(int nLen)

{

std::string strRet;

const char* cArray = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

int nArrayLen = strlen(cArray);

//设置当前时间为种子

srand(::time(NULL));

for (int i = 0; i < nLen; i++)

{

int nIndex = rand() % nArrayLen;

strRet += cArray[nIndex];

}

return strRet;

}

(二)解决方案

1.使用GUID来生产随机字符串,保证多线程下是安全的。这种方法和函数的本意虽然有些偏离,但是在软件中这个确实是解决了问题。那如果一定要保持函数原型不变,需要生成有数字和字母组成的随机的指定字符个数的字符串,该怎么办呢?第二种方法可以解决。

string GenerateRandomString()

{

GUID guid;

::CoInitialize(NULL);

CoCreateGuid(&guid);

CHAR buff[65] = {0};

StringCchPrintfA(

buff, 64, ("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"),

guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1],

guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5],

guid.Data4[6], guid.Data4[7]);

return string(buff);

}

2.使用rand_s代替rand函数,这个函数是不依赖种子的更安全的伪随机数生成器。并且是线程安全的。

string GenerateRandomKey(int nLen)

{

std::string strRet;

const char* cArray = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

int nArrayLen = strlen(cArray);

for (int i = 0; i < nLen; i++)

{

unsigned uRandomValue = 0;

rand_s(&uRandomValue);

int nIndex = uRandomValue % nArrayLen;

strRet += cArray[nIndex];

}

return strRet;

}

(三)rand以及rand_s函数的实现(vs2017)

1.rand函数实现

// Seeds the random number generator with the provided integer.

extern "C" void __cdecl srand(unsigned int const seed)

{

__acrt_getptd()->_rand_state = seed;

}

// Returns a pseudorandom number in the range [0,32767].

extern "C" int __cdecl rand()

{

__acrt_ptd* const ptd = __acrt_getptd();

ptd->_rand_state = ptd->_rand_state * 214013 + 2531011;

return (ptd->_rand_state >> 16) & RAND_MAX;

}

可以看出,srand函数设置的种子保存在线程本地存储(TLS)中的_rand_state中,而rand函数就是对这个值进行了简单的数学运算然后又赋值给了_rand_state。可以看出这种方式生成的随机数是确定的,也就是说相同的种子返回相同的随机数序列。

从这里也看到了随机数的种子是和线程相关的,每个线程是隔离的,多线程代码执行确实是安全的。但是问题是种子如果一样了生成的随机数就一样了。

顺便提下_rand_state的默认值是1

2.rand_s函数实现

extern "C" errno_t __cdecl rand_s(unsigned int* const result)

{

_VALIDATE_RETURN_ERRCODE(result != nullptr, EINVAL);

*result = 0;

if (!__acrt_RtlGenRandom(result, static_cast(sizeof(*result))))

{

errno = ENOMEM;

return errno;

}

return 0;

}

函数只是简单调用了下RtlGenRandom函数而已。

Generates a pseudorandom number. This is a more secure version of the function rand

也就是说他是一个安全版本的伪随机数。(还是伪随机数哦)

为什么更安全,我没怎么明白原理。总体来说就是不能再依赖种子了。所以更安全了

965773c429247714900865e81060a191.png

对比java来说:

rand相当于Random类

rand_s相当于SecureRandom

(四)真随机数和伪随机数

(五)参考资料

https://www.random.org/

https://blog.csdn.net/czc1997/article/details/78167705

https://blog.csdn.net/hahachenchen789/article/details/84251345

https://www.iteye.com/blog/kevin-hust-744284

原文:https://www.cnblogs.com/1yzq/p/13121781.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值