zoukankan      html  css  js  c++  java
  • Linux 信号量 生产者消费者小例题

      菜鸟偶遇信号量,擦出火花(只有不熟才会有火花)。于是上网搜资料和看《Unix环境高级编程》实现了几个小例题,高手请勿喷!这几位写得非常好啊:

    题目来源: http://www.it165.net/os/html/201312/7039.html

    信号量及其用法:http://www.cnblogs.com/hjslovewcl/archive/2011/03/03/2314341.html

    Mutex与Semaphore区别著名的厕所理论:http://koti.mbnet.fi/niclasw/MutexSemaphore.html

       哎呀,暴露了!我不是故意偷窥别人的……




    一:一个生产者、一个消费者、一个资源情况

      这种情况情况可以只用一个信号量,要生成或要消费只用尝试获取这个信号量,这里用了两个:full=1和empty=0,两个只为了和后面一致,1、0是赋初值。生产者和消费者情况如下:

    //生产者:
    P(empty)
        生成资源并放进资源处
    V(full)
    
    
    //消费者:
    P(full)
        消费资源
    V(empty)

      若生产者最先开始生产资源,P(empty),full和empty都成了0,此时若消费者想要消费,则P(full)时,full为0则睡眠等待,等生产者生结束就把full加1,看到消费者可怜地睡着了就唤醒它,然后消费者把full减1自己快活去了。

      消费者消费过程中生产者若要生了,则会因为empty为0而休眠,等消费者结束就把empty加1,然后生产者开始生产。

      上面的好理解,下面上代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    
    #include <x86_64-linux-gnu/sys/types.h>
    #include <x86_64-linux-gnu/sys/ipc.h>
    #include <x86_64-linux-gnu/sys/sem.h>
    
    int semInite(int semId, int value);
    int semDelete(int semId);
    int semP(int semId);
    int semV(int semId);
    
    //declare a union to be used
    union semun 
    {
        int val;                        /* value for SETVAL */ 
        struct semid_ds *buf;                /* buffer for IPC_STAT, IPC_SET */ 
        unsigned short int *array;         /* array for GETALL, SETALL */ 
        struct seminfo *__buf;                /* buffer for IPC_INFO */ 
    };
    
    
    //semaphore declare
    static int semFullId;
    static int semEmptyId;
    static int source = 0;        //source definition    
    
    
    //new thread as a consumer
    void* child_thread(void* arg)
    {
        int ttt = 1;
            
        while(1)
        {
            sleep(rand() % 19);
            printf("child No.%d times wants to consume...
    ", ttt);
            
            semP(semFullId);    //
            printf("child No.%d times start consuming. source = %d
    ", ttt, source);
            source = 0;
            printf("child No.%d times end consuming. source = %d
    
    ", ttt++, source);
            semV(semEmptyId);    //
        }
        
        return (void*)0;
    }
    
    int main(void)
    {    
        //create semaphore
        semFullId = semget((key_t)1235, 1, 0666 | IPC_CREAT);
        semEmptyId = semget((key_t)1236, 1, 0666 | IPC_CREAT);
        semInite(semFullId, 0);
        semInite(semEmptyId, 1);
        
        pthread_t pid;
        pthread_create(&pid, NULL, child_thread, NULL);
        
        int tt = 1;    
        while(1)
        {
            sleep(rand() % 18);
            printf("parent No.%d times wants to produce...
    ", tt);
            
            semP(semEmptyId);    //
            printf("parent No.%d times start producing. source = %d
    ", tt, source);
            source = rand() % 100;
            printf("parent No.%d times end producing. source = %d
    ", tt++, source);
            semV(semFullId);    //
        }
        
        semDelete(semFullId);
        semDelete(semEmptyId);
        return 0;
    }
    
    
    //set semaphore as default value
    int semInite(int semId, int value)
    {
        union semun semUnion;
        semUnion.val = value;    //set default semaphore
        return semctl(semId, 0, SETVAL, semUnion);
    }
    
    //delete semaphore
    int semDelete(int semId)
    {
        union semun semUnion;
        return semctl(semId, 0, IPC_RMID, semUnion);
    }
    
    //semaphore P operation
    int semP(int semId)
    {
        struct sembuf semBuf;
        semBuf.sem_num = 0;        //indicate it is not semaphore array
        semBuf.sem_op = -1;        //subtract one
        semBuf.sem_flg = SEM_UNDO;
        
        return semop(semId, &semBuf, 1);    //return value
    }
    
    //semaphore V operation
    int semV(int semId)
    {
        struct sembuf semBuf;
        semBuf.sem_num = 0;        //indicate it is not semaphore array
        semBuf.sem_op = 1;        //subtract one
        semBuf.sem_flg = SEM_UNDO;
        
        return semop(semId, &semBuf, 1);    //return value
    }
    111

    两个信号量其实应该用信号量集的,因为它本来就是针对集合的,但是由于刚入门,为了易懂,就用两个。两个线程,创建的新线程当做消费者了。其中unix的几个信号量的函数看了半天,有点复杂,简单不准确来讲:

    //获得一个信号量啦,第二个参数是想要创建的信号量个数,
    //因为unix操作的是信号量集合,设为1不就一个信号量了嘛
    //其他参数我不管了
    int semget(key_t key, int num_sems, int sem_flags);
    
    //信号量集合的操作,这个可以用来实现P、V的 +1 -1 的功能
    int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
    
    //信号量集合的控制,如初始化删除等
    int semctl(int sem_id, int sem_num, int command, ...);

    运行:




    二:一个生产者、一个消费者、N个资源情况

      这里资源用是一个数组代替了。其实本质上和上面类似,每次只让生产者或消费者中的一个进入,进入后放到哪个地方或从哪个地方取就得用一个标志来说明了,其实也可以为每一资源加上信号量的。

      这里在生产者和消费者那里都设置了一个static的变量当做游标,指示下个资源放到哪个位置和下次从哪取资源。staitic变量用在这里很合适,因为只会初始化一次。

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    
    #include <x86_64-linux-gnu/sys/types.h>
    #include <x86_64-linux-gnu/sys/ipc.h>
    #include <x86_64-linux-gnu/sys/sem.h>
    
    #define N 5
    
    int semInite(int semId, int value);
    int semDelete(int semId);
    int semP(int semId);
    int semV(int semId);
    
    //declare a union to be used
    union semun 
    {
        int val;                        /* value for SETVAL */ 
        struct semid_ds *buf;                /* buffer for IPC_STAT, IPC_SET */ 
        unsigned short int *array;         /* array for GETALL, SETALL */ 
        struct seminfo *__buf;                /* buffer for IPC_INFO */ 
    };
    
    
    //semaphore declare
    static int semFullId;
    static int semEmptyId;
    static int srcArr[N];        //source definition    
    
    
    //new thread as a consumer
    void* child_thread(void* arg)
    {
        int ttt = 1;
            
        while(1)
        {
            static int pToGet = 0;    //get source from the position
            sleep(rand() % 19);
            printf("child No.%d times wants to consume(get from index %d)...
    ", ttt, pToGet);
            
            semP(semFullId);    //
            printf("child No.%d times start consuming.(get from index %d, data is %d)
    ", ttt, pToGet, srcArr[pToGet]);
            srcArr[pToGet] = 0;
            printf("child No.%d times end consuming. (get from index %d)
    
    ", ttt++, pToGet);
            pToGet = (pToGet + 1) % N;
            semV(semEmptyId);    //
        }
        
        return (void*)0;
    }
    
    int main(void)
    {    
        //create semaphore
        semFullId = semget((key_t)1235, 1, 0666 | IPC_CREAT);
        semEmptyId = semget((key_t)1236, 1, 0666 | IPC_CREAT);
        semInite(semFullId, 0);
        semInite(semEmptyId, N);    //N source
        
        pthread_t pid;
        pthread_create(&pid, NULL, child_thread, NULL);
        
        int tt = 1;    
        while(1)
        {
            static int pToPut = 0;        //next position where source to be filled in
            sleep(rand() % 18);
            printf("parent No.%d times wants to produce(put in %d index)...
    ", tt, pToPut);
            
            semP(semEmptyId);    //
            printf("parent No.%d times start producing.(put in %d index, original data is %d)
    ", tt, pToPut, srcArr[pToPut]);
            int temp = rand() % 100;
            srcArr[pToPut] = temp;
            printf("parent No.%d times end producing.(put in %d index, now data is %d)
    ", tt++, pToPut, srcArr[pToPut]);
            pToPut = (pToPut + 1) % N;
            semV(semFullId);    //
        }
        
        semDelete(semFullId);
        semDelete(semEmptyId);
        return 0;
    }
    
    
    //set semaphore as default value
    int semInite(int semId, int value)
    {
        union semun semUnion;
        semUnion.val = value;    //set default semaphore
        return semctl(semId, 0, SETVAL, semUnion);
    }
    
    //delete semaphore
    int semDelete(int semId)
    {
        union semun semUnion;
        return semctl(semId, 0, IPC_RMID, semUnion);
    }
    
    //semaphore P operation
    int semP(int semId)
    {
        struct sembuf semBuf;
        semBuf.sem_num = 0;        //indicate it is not semaphore array
        semBuf.sem_op = -1;        //subtract one
        semBuf.sem_flg = SEM_UNDO;
        
        return semop(semId, &semBuf, 1);    //return value
    }
    
    //semaphore V operation
    int semV(int semId)
    {
        struct sembuf semBuf;
        semBuf.sem_num = 0;        //indicate it is not semaphore array
        semBuf.sem_op = 1;        //subtract one
        semBuf.sem_flg = SEM_UNDO;
        
        return semop(semId, &semBuf, 1);    //return value
    }
    222

    运行结果:




    三:N个生产者,N个消费者,N个资源

       这种情况不仅生产者和消费者之间要通过上述的方式协调使用资源,而且生产者内部和消费者内部也要协调。定义四个信号量:

    empty——表示缓冲区是否为空,初值为n。
    full——表示缓冲区中是否为满,初值为0。
    mutex1——生产者之间的互斥信号量,初值为1。
    mutex2——消费者之间的互斥信号量,初值为1。

    //生产者进程
    P(mutex1)
        P(empty)
            生产数据并放进特定位置
        V(full)
    V(mutex1)
    
    //消费者进程
    P(mutex2)
        P(full)
            消费数据
        V(empty)
    V(mutex2)

    其实上面生产者或者消费者获取互斥量或信号量的顺序可以颠倒的,不会产生死锁。

      当然这个问题可以用其他更好的方式解决,我还得继续学习。

  • 相关阅读:
    几种委托的解释
    Python中的编码风格
    Python的循环
    Python中操作文件
    Python的random模块、string模块、time模块、os模块
    Python中的函数
    Python的数据类型
    使用iview Form 的resetFields()在f12下报错
    滚动条的滚动距离
    编程学习之资源
  • 原文地址:https://www.cnblogs.com/jiayith/p/3854312.html
Copyright © 2011-2022 走看看