C++多线程下的rand()问题

 

【问题描述】
在主线程里面srand()了一次,然后在线程函数里面使用rand(),可是每个线程rand出来的数列顺序一样,而且每次运行都一样。结构如图所示:

func1出来的序列是1,7,4,0,9,4,8……
func2出来的序列是1,7,4,0,9,4,8……

【讨论】
网上有人说这是因为main的seed是time,而两个线程是几乎并发开始的,它们继承了main的seed,因此输出序列相同。
【证伪】
[1]将main中的随机种子设为srand(1),其余不做改变。两个线程输出结果仍旧是1,7,4,0,9,4,8……
这说明main中的种子与线程中并无关系。
[2]为了巩固以上假设,进行一下测试:
main中srand(time),在func1和func2里面均加上语句srand(1),输出序列均为1,7,4,0,9,4,8……
main中srand(time),在func1和func2里面均加上语句srand(time),输出序列均为2,9,4,6,8,3,7……
main中srand(1),在func1和func2里面均加上语句srand(time),输出序列均为2,9,4,6,8,3,7……
main中srand(1),在func1里加上srand(1),func2里面加上srand(time),输出序列分别为1,7,4,0,9,4,8……和2,9,4,6,8,3,7……
——由此,证明了无论main中的srand是如何的,线程中的rand结果只与该线程中的srand有关。也即,多线程编程中,在每个线程里面都要srand一次。
说明:由于如果没有显式srand,系统默认为rand(1),因此在func里面用不用该语句输出都一样。当然,如果两个线程中都srand了time,由于它们并发运行,也就是说执行srand的时候它们的time相同,即种子相同,从而输出序列相同。

【问题解决】
如何做到不同线程中的数从横向上看来也不同呢?
先从rand的伪随机性说起。
我们都说rand是伪随机的,是因为它有章可循,并不是真的“随机”。它是以某个线性同余的递推公式推算出来的一些列数,当这系列数很大的时候,它们就符合正态分布。这个递推公式类似于:
y=ax+b(mod   n),其中,a、n一般是大素数,而a、b、n都是常数,因此rand的产生决定于x,而且下一个y是上一次产生的y的函数。
-----------------------------------------------------------------------
比如VC中对于rand()函数是如下实现的:
int   __cdecl   rand   (void)  
{  return(((holdrand   =   holdrand   *   214013L   +   2531011L)   >>   16)   &   0x7fff);   }
-------------------------------------------------------------------------
而srand()的函数是如下实现的:
void __cdecl srand (unsigned int seed)
{
  #ifdef _MT 
    _getptd()->_holdrand = (unsigned long)seed;  
  #else /* _MT */
   holdrand = (long)seed;
  #endif /* _MT */
}
-------------------------------------------------------------------------
递推公式意味着,确定的输入产生确定的输出,而且顺序是不变的。因此,当我们设置srand()的时候,相当于设置了递推公式的数列起点,从x开始计算y。因此,srand的种子相同相当于起点相同,数列相同也就可以理解了。
那么,如果要让两个数列不同,只需要srand的时候使用不同seed就可以了。可以采取以下方法:
1)在外部产生随机数,然后将该数作为参数传进线程函数设为seed。
2)将线程ID作为线程的seed。
3)使用其他可以令线程拥有不同seed的方法。
*)最好不要使用time(NULL)作为每个线程的seed,因为线程开始的time(NULL)很可能相同。

附:getptd函数:
-------------------------------------------------------------------------
_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);
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在C语言中实现多线程的生产者消费者模型可以使用线程库pthread。下面是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #define BUFFER_SIZE 5 int buffer[BUFFER_SIZE]; int count = 0; // 缓冲区中的数据数量 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 互斥锁 pthread_cond_t empty = PTHREAD_COND_INITIALIZER; // 缓冲区为空的条件变量 pthread_cond_t full = PTHREAD_COND_INITIALIZER; // 缓冲区为满的条件变量 void* producer(void* arg) { int item; while (1) { item = rand() % 100; // 生成随机数作为生产的物品 pthread_mutex_lock(&mutex); // 加锁 while (count == BUFFER_SIZE) { // 如果缓冲区已满,等待 pthread_cond_wait(&empty, &mutex); } buffer[count++] = item; // 将物品放入缓冲区 printf("Producer produced item: %d\n", item); pthread_cond_signal(&full); // 唤醒等待的消费者 pthread_mutex_unlock(&mutex); // 解锁 } return NULL; } void* consumer(void* arg) { int item; while (1) { pthread_mutex_lock(&mutex); // 加锁 while (count == 0) { // 如果缓冲区为空,等待 pthread_cond_wait(&full, &mutex); } item = buffer[--count]; // 从缓冲区取出物品 printf("Consumer consumed item: %d\n", item); pthread_cond_signal(&empty); // 唤醒等待的生产者 pthread_mutex_unlock(&mutex); // 解锁 } return NULL; } int main() { pthread_t producer_thread, consumer_thread; pthread_create(&producer_thread, NULL, producer, NULL); pthread_create(&consumer_thread, NULL, consumer, NULL); pthread_join(producer_thread, NULL); pthread_join(consumer_thread, NULL); return 0; } ``` 这个示例代码中,定义了一个大小为5的缓冲区(使用数组实现),其中`count`变量表示缓冲区中的数据数量。生产者线程通过生成随机数作为物品,并将物品放入缓冲区。消费者线程从缓冲区中取出物品并进行消费。互斥锁`mutex`用于保护临界区资源的访问,条件变量`empty`和`full`用于实现生产者和消费者之间的同步。 请注意,这只是一个简单的示例代码,没有考虑线程安全性和错误处理。在实际使用中,还需要更加细致的设计和处理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值