阿里2014校招笔试题(南大)——利用thread和sleep生成字符串的伪随机序列

引言:题目具体描述记不大清了,大概是:Linux平台,利用线程调度的随机性和sleep的不准确性,生成一个各位均不相同的字符数组的伪随机序列。不得使用任何库函数。(这句记得清楚,当时在想线程库算不算可怜,题目的意思应该是:不得使用库提供的随机函数)

1.算法

当时读完题很开心,这题可以用与“《编程珠玑》取样问题(ch12, p119)”类似的算法解决。算法如下——除了第一字符(下标0)以外,为其余N-1个字符各创建一个线程,每个线程先sleep一秒(也可以更长),再将对应位置的字符和第一个字符交换;N-1个线程完成后,主线程结束原理暗藏在题目中,sleep一秒后,因为sleep的不准确性,这N-1个线程几乎同时醒来(就绪)(试想如若sleep非常精确,各个线程醒来的顺序就会和创建顺序相同);又由于线程调度的随机性,这时会被执行的线程是随机的,(不知先后顺序地)执行N-1次之前所述的交换所得的便是一个伪随机序列。不过当时想不起来pthread_create几个参数的顺序了(前面题的计算量不小,头都搞晕了),就随便按个顺序写了。回来后,按照当时的思路很快在电脑上写了出来:

#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        

#ifdef WIN32
# include 
        
        
          #define sleep(x) Sleep(1000 * x) #endif char str[] = "ABCDEFGH"; #define LEN sizeof(str) / sizeof(str[0]) - 1 pthread_t tids[LEN]; void* thread_func(void *idx) { char tmp; sleep(1); tmp = str[(int)idx]; str[(int)idx] = str[0]; // str[0] is the critical resource !! str[0] = tmp; return (void*)0; } int main(void) { int i; puts(str); for(i=1; i 
          
        
       
       
      
      
     
     
    
    
注意:main里用sleep(2)(比N-1个线程多sleep一秒)等待其他线程只是为了写起来简单,并不严谨!

2.互斥

不过一在电脑上写出来立即意识到一个问题——第一个元素是临界资源(所有“其他线程”都想抢着用这块地);如果不做互斥访问可能会出现——有的字符出现两次(获更多)有的字符没了,这种错误不是每次都会出现:


意识到这个错误之后,比较容易修改,只需将线程函数内对第一个元素的操作放入临界区中即可:

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       

#ifdef WIN32
# include 
       
       
         #define sleep(x) Sleep(1000 * x) #endif char str[] = "ABCDEFGH"; #define LEN sizeof(str) / sizeof(str[0]) - 1 pthread_t tids[LEN]; pthread_mutex_t mutex; void* thread_func(void *idx) { char tmp; sleep(1); tmp = str[(int)idx]; pthread_mutex_lock(&mutex); str[(int)idx] = str[0]; // str[0] is the critical resource !! str[0] = tmp; pthread_mutex_unlock(&mutex); return (void*)0; } int main(void) { int i; puts(str); pthread_mutex_init(&mutex, NULL); for(i=1; i 
         
       
      
      
     
     
    
    
   
   

这次不会再有错误。for命令连续测试10次:


到此,说明这个算法没有问题。

3.同步

还应该main里的sleeep(2)改掉。main创建好N-1个线程后就应该被挂起,直到其他所有线程都“完工”后才应该被唤醒;肯定要用条件变量,main创建好其他线程后wait阻塞,其他所有线程都“完工”再被signal唤醒。为了保证这样的顺序,必须要让最后一个完工的线程知道自己是最后一个,也就是在最后一个其他线程“收工”的时候signal。只需要加个计数变量count用来标记还有多少线程没有完工,将其初始化为要创建的线程数N-1,每有一个线程“完工”就count--,判断count的值即可知道当前线程是倒数第几个完工的。(啰嗦一堆,还是上代码直观):

#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
         
         

#ifdef WIN32
# include 
         
         
           #define sleep(x) Sleep(1000 * x) #endif char str[] = "ABCDEFGH"; #define LEN sizeof(str) / sizeof(str[0]) - 1 int count; pthread_t tids[LEN]; pthread_mutex_t mutex; pthread_cond_t cond; void* thread_func(void *idx) { char tmp; // in TLS(Thread local space.) sleep(1); tmp = str[(long)idx]; pthread_mutex_lock(&mutex); str[(long)idx] = str[0]; // str[0] is the critical resource. str[0] = tmp; --count; printf("%ld %d\n", (long)idx, count); if(count == 0) { puts("SIGNAL"); pthread_cond_signal(&cond); } pthread_mutex_unlock(&mutex); return (void*)0; } int main(void) { long i; puts(str); count = LEN-1; pthread_mutex_init(&mutex, 0); pthread_cond_init(&cond, 0); for(i=1; i 
          
            0) { pthread_cond_wait(&cond, &mutex); } pthread_mutex_unlock(&mutex); puts(str); pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex); return EXIT_SUCCESS; } 
           
         
        
        
       
       
      
      
     
     
需要稍加留意的是:count也是临界资源,需要放到临界区里(和str[0]一样);另外pthread_cond_wait接口所需的pthread_mutex_t * 不能用维护“其他线程”临界的那个mutex,应该再定义一个mutex。因为pthread_cond_wait会原子性地阻塞当前线程同时unlock传入的mutex,pthread_cond_wait返回时,传入的mutex再次被锁。(APUEcn2e p309)

注:本机环境gcc 4.6.1(MinGW)  


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码工许师傅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值