• 利用thread和sleep生成字符串的伪随机序列


    原文:http://blog.csdn.net/xusiwei1236/article/details/11990591

    ps:互联网上牛人真多!想想自己自己借到 编程珠玑 几个月了一页没翻,(ˇˍˇ) 哎~

    pps:3.29日,和小伙伴讨论这个问题的时候发觉,原文中用的随机算法不是严格随机,细心地童鞋可能会发现,按原作者的随机算法使得随机之后的字符串中,字符"A"永远也不会出现在第一个位置上,但是这样的话就造成不是严格随机,以下是改进方法:可以再增加一个全局buffer,当某个线程第一个被唤醒,把相应的字符写到buffer[0],当某个线程第二个被唤醒,把相应的字符写到buffer[1],以此类推,这样最后buffer里面的字符排列大致是随机的,比原文中随机算法更合理。

    1.算法

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

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    
    #ifdef WIN32
    # include <windows.h>
    #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<LEN; i++) {
            pthread_create(&tids[i], NULL, thread_func, (void*)i);
        }
        
        sleep(2);
        
        puts(str);
    
        return 0;
    }

    注意:main里用sleep(2)(比N-1个线程多sleep一秒)等待其他线程只是为了写起来简单,并不严谨!

    2.互斥

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

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

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    
    #ifdef WIN32
    # include <windows.h>
    #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<LEN; i++) {
            pthread_create(&tids[i], NULL, thread_func, (void*)i);
        }
        
        sleep(2);
        
        puts(str);
        
        pthread_mutex_destroy(&mutex);
    
        return EXIT_SUCCESS;
    }

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

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

    3.同步

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

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    
    #ifdef WIN32
    # include <windows.h>
    #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_mutex_t mc;
    pthread_cond_t  cond;
    
    void* thread_func(void *idx)
    {
        char tmp; // in TLS(Thread local space.)
    
        sleep(1);
    
        tmp = str[(int)idx];
        pthread_mutex_lock(&mutex);
        str[(int)idx] = str[0]; // str[0] is the critical resource.
        str[0] = tmp;
        --count;
        printf("%d %d
    ", idx, count);
        if(count == 0) {
            puts("SIGNAL");
            pthread_cond_signal(&cond);
        }
        pthread_mutex_unlock(&mutex);
    
        return (void*)0;
    }
    
    int main(void)
    {
        int i;
    
        puts(str);
    
        count = LEN-1;
        pthread_mutex_init(&mutex, 0);
        pthread_mutex_init(&mc, 0);
        pthread_cond_init(&cond, 0);
    
        for(i=1; i<LEN; i++) {
            pthread_create(&tids[i], 0, thread_func, (void*)i);
        }
    
        pthread_mutex_lock(&mc);
        puts("WAIT");
        pthread_cond_wait(&cond, &mc);
        pthread_mutex_unlock(&mc);
    
        puts(str);
    
        pthread_cond_destroy(&cond);
        pthread_mutex_destroy(&mc);
        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)  

  • 相关阅读:
    排列组合
    分治——最大连续数组和
    分治——最近点对
    Java数据类型
    4源代码的下载和编译
    3Git使用入门
    2.3搭建Android应用程序开发环境
    2.2安装JDK
    2.1Android底层开发需要哪些工具
    1.8小结
  • 原文地址:https://www.cnblogs.com/theCambrian/p/3631502.html
Copyright © 2020-2023  润新知