美文网首页
1-6linux系统编程——线程间通信——练习:模拟银行的排队系

1-6linux系统编程——线程间通信——练习:模拟银行的排队系

作者: 赋闲 | 来源:发表于2017-01-08 15:56 被阅读0次

    线程间通信

    信息交换:

    使用多个线程都可见的内存区域

    线程互斥锁:

    保障有同一把锁保护的共享资源被多个线程互斥访问
    互斥锁:pthread_mutex_t
    互斥锁初始化:pthread_mutex_init
    互斥锁的获取(加锁)pthread_mutex_lock
    互斥锁的释放(解锁)pthread_mutex_unlock

    线程信号量:

    解决多个线程在使用有限资源时的同步问题
    线程信号量:sem_t
    信号量的初始化:sem_init
    信号量的获取:sem_wait
    信号量的释放:sem_post
    信号量的销毁:sem_destory

    原子性操作:

    或全执行,或全不执行。

    逻辑图

    ](https://img.haomeiwen.com/i3821352/0f5492e734fe2fc9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    举个例子:模拟排队系统,单运程多线程

    新建一个自定义头文件,存放链表结构体

    #ifndef __SQ_QUEUE_H__
    #define __SQ_QUEUE_H__
    
    // 定义链式队列中元素的实际类型的别名
    // 注意:队列中可以存放任意类型的数据
    typedef int datatype_t ;
    
    // 定义链式队列中结点的类型及其别名
    typedef struct _queue_node
    {   
        datatype_t data;
        struct _queue_node *next;    
    }queue_node_t;
    
    // 定义存放链式队列队首和队尾指针的结构类型及其别名
    typedef struct _link_queue
    {
        queue_node_t *front;
        queue_node_t *rear;
    }link_queue_t;
    
    // 创建一个空的链式队列
    link_queue_t *create_empty_link_queue(void);
    
    // 判断链式队列是否为空,为空返回1,否则,0
    int is_empty_of_link_queue(link_queue_t *queue);
    
    // 求链式队列中元素的个数即队列长度
    int length_of_link_queue(link_queue_t *queue);
    
    // 入队操作(即在队尾插入数据结点)
    int insert_to_link_queue(link_queue_t *queue, 
                datatype_t x);
    
    // 出队操作(即在对头删除数据结点)
    int delete_from_link_queue(link_queue_t *queue, 
                datatype_t *px);
    
    // 清空链式队列
    void clear_link_queue(link_queue_t *queue);
    
    // 销毁链式队列
    void destroy_link_queue(link_queue_t *queue);
    
    // 遍历打印链式队列中的数据
    void print_data_info_of_link_queue(link_queue_t *queue);
    
    #endif
    

    对链表的操作:新建链表、尾插、头删、清空数据、判断链表长度、删除链表

    #include "link_queue.h"
    #include <stdlib.h>
    #include <stdio.h>
    
    // 创建链式队列
    link_queue_t *create_empty_link_queue(void)
    {
        // 为包含队首和队尾指针的结点动态申请空间
        link_queue_t *queue = (link_queue_t *)malloc(sizeof(link_queue_t));
        // 队首指针和队尾指针均指向头结点
        queue->front = queue->rear = (queue_node_t *)malloc(sizeof(queue_node_t));
        queue->front->next = NULL;
        
        return queue;
    }
    
    // 判断链式队列是否为空,为空返回1,否则0
    int is_empty_of_link_queue(link_queue_t *queue)
    {
        return queue->front == queue->rear;
        // return queue->front->next == NULL;
    }
    
    // 求链式队列的数据元素个数即队列长度
    int length_of_link_queue(link_queue_t *queue)
    {
        int len = 0;
        queue_node_t *p = queue->front;
        
        while(p->next != NULL)
        {
            len++;
            p = p->next;
        }
        
        return len;
    }
    
    // 入队操作,成功返回0,失败-1
    int insert_to_link_queue(link_queue_t *queue, 
                datatype_t x)
    {
        // 队列不存在,插入失败返回
        if(queue == NULL)
            return -1;
        
        // 1.为插入的数据申请结点空间
        queue_node_t *p = (queue_node_t *)malloc(sizeof(queue_node_t));
        p->data = x;
        
        // 2.插入到队尾指针的后一个位置
        p->next = queue->rear->next;
        queue->rear->next = p;
        
        // 3.更新队尾指针
        queue->rear = queue->rear->next;
        
        return 0;
    }
    
    // 出队操作,成功,返回0;失败,-1
    int delete_from_link_queue(link_queue_t *queue, 
                datatype_t *px)
    {
        // 如果队列不存在或队列为空,出队失败
        if(queue == NULL || is_empty_of_link_queue(queue))
            return -1;
        
        // 1.定位出队的数据结点的位置
        queue_node_t *p = queue->front->next;//头删
        // 2.将该结点从链式队列中移除
        queue->front->next = p->next;
        // 3.如果需要,将待出队元素的值传出
        if(px != NULL)
            *px = p->data;
        // 4.释放结点空间
        free(p);
        
        // 注意:如果出队之后,队列为空,重定位尾指针的位置
        if(queue->front->next == NULL)
            queue->rear = queue->front;
        
        return 0;
    }
    
    // 清空链式队列
    void clear_link_queue(link_queue_t *queue)
    {
        queue_node_t *p = NULL;
    
        // 循环的采用头删法删除链式队列的数据结点直到队列为空
        while(!is_empty_of_link_queue(queue))
        {
            p = queue->front->next;
            queue->front->next = p->next;
            free(p);
        }
    }
    
    // 销毁链式队列
    void destroy_link_queue(link_queue_t *queue)
    {
        // 清空链式队列
        clear_link_queue(queue);
        // 释放链式队列头结点空间
        free(queue->front);
        // 释放队首和队尾指针结点空间
        free(queue);
    }
    
    // 根据实际datatype_t类型打印数据
    static void print_data(datatype_t x)
    {
        printf("%d ", x);
    }
    
    // 遍历打印链式队列的数据结点
    void print_data_info_of_link_queue(link_queue_t *queue)
    {
        queue_node_t *p = queue->front;
        
        printf("linkqueue : ");
        while(p->next != NULL)
        {
            p = p->next;
            print_data(p->data);    
        }    
        printf("\n");
    }
    

    产生链表,随时间间隔插入和删除,有互斥锁

    // 练习:模拟银行的排队系统
    
    #include <stdio.h>
    #include "link_queue.h"
    #include <pthread.h>
    #include <unistd.h>
    #include <semaphore.h>
    
    #define MAX_CLIENT_COUNT 100
    
    // 全局队列:用于存放用户ID的链式队列
    link_queue_t *queue;
    // 互斥锁:用于保障生产者线程和消费者线程互斥访问用户队列
    pthread_mutex_t mutex;
    // 用户数信号量:用于表示当前可用的用户个数
    sem_t client_sem;
    // 空余排队数信号量:用于表示当前队列中能够继续插入的用户个数
    sem_t free_space_sem;
    
    // 生产者线程:用于模拟用户的产生
    void *producer_thread(void *arg);
    
    // 消费者线程:用于模拟银行柜台的动作
    void *consumer_thread(void *arg);
    
    int main(int argc, char *argv[])
    {
        pthread_t producer_id;
        pthread_t consumer_id;
    
        // 初始化全局用户队列
        queue = create_empty_link_queue();
        // 初始化互斥锁
        pthread_mutex_init(&mutex, NULL);
        // 初始化相关信号量
        sem_init(&client_sem, 0, 0);
        sem_init(&free_space_sem, 0, MAX_CLIENT_COUNT);//限制队列长度
        
        // create threads
        pthread_create(&producer_id, NULL, producer_thread, NULL);
        pthread_create(&consumer_id, NULL, consumer_thread, NULL);
        
        // join threads 
        pthread_join(producer_id, NULL);//用户
        pthread_join(consumer_id, NULL);//柜台
    
        sem_destroy(&client_sem);
        sem_destroy(&free_space_sem);//销毁由sem指向的信号量
    
        return 0;
    }
    
    // 生产者线程:用于模拟用户的产生
    void *producer_thread(void *arg)
    {
        int client_no = 0;
        
        while(1)
        {
            // 1.获取产生用户的资格(即让空余位置-1)
            sem_wait(&free_space_sem);
            
            // 2.模拟产生用户ID
            client_no = rand()%1000+1;//1~1000
            
            // 3.以互斥的方式向用户队列中插入用户ID信息
                pthread_mutex_lock(&mutex);
            insert_to_link_queue(queue, client_no);
                pthread_mutex_unlock(&mutex);
            
            // 4.模拟打印用户入队信息
            printf("client %d is in...queue length : %d\n", client_no, length_of_link_queue(queue));
            
            // 5.可用用户数目+1
            sem_post(&client_sem);
            
            // 6.模拟用户的到达时间间隔
            sleep(1);
        }
    }
    
    // 消费者线程:用于模拟银行柜台的动作
    void *consumer_thread (void *arg)
    {
        int client_no = 0;
        
        while (1)
        {
            // 1.申请可用用户资源(即对可用用户数-1)
            sem_wait(&client_sem);
            
            // 2.以互斥的方式从全局用户队列中获取用户ID
            pthread_mutex_lock(&mutex);
            delete_from_link_queue(queue, &client_no);
            pthread_mutex_unlock(&mutex);
            
            // 3.模拟银行柜台的操作
            printf("client %d is out...\n", client_no);  
            
            // 3.队列空余资源数+1
            sem_post(&free_space_sem);
            
            // 4.模拟柜台操作的时间间隔
            sleep(rand()%5+1);
        }
    }
    

    相关文章

      网友评论

          本文标题:1-6linux系统编程——线程间通信——练习:模拟银行的排队系

          本文链接:https://www.haomeiwen.com/subject/gnlxbttx.html