zoukankan      html  css  js  c++  java
  • pthread和semaphore的简单应用以及四个典型的多线程问题

    pthread和semaphore的简单应用以及四个典型的多线程问题

    pthread常用函数简单介绍

    创建线程

    int  pthread_create(pthread_t  *  thread,

    pthread_attr_t * attr,

    void * (*start_routine)(void *),

    void * arg)

    thread是一个pthread_t类型的指针,可以简单理解为线程ID

    attr表示该线程的属性,具体没有看,下面的程序中都设置成了NULL,表示默认属性。

    start_routine是线程函数体的函数指针

    arg是线程函数的参数

    线程函数的类型是 void *fun(void *)也就是可以带一个指针参数,也可以返回一个指针。

    父线程回收子线程资源

    当子线程运行结束后,还有以下资源要回收。

    int pthread_join(pthread_t th, void **thread_return)

    th使pthread_t类型的变量,可以理解为线程ID

    thread_return 是子线程函数的返回值。

    初始化一个互斥锁

    int pthread_mutex_init(pthread_mutex_t *mutex,

    const pthread_mutex_attr_t *mutexattr);

    mutex表示待初始化的互斥锁,mutexattr表示互斥锁的属性,没仔细研究,下面的程序中都是使用的NULL。表示默认属性

    互斥锁枷锁和解锁

    int pthread_mutex_lock(pthread_mutex *mutex);

    int pthread_mutex_unlock(pthread_mutex *mutex);

    销毁互斥锁

    int pthread_mutex_destroy(pthread_mutex *mutex);

    semaphore常用函数介绍

    初始化信号量

    int sem_init (sem_t *sem , int pshared, unsigned int value);

    sem表示待初始化的信号量

    pshared表示共享属性,Linux中貌似只能设置为0

    value表示信号量的初始值

    申请资源

    int sem_wait(sem_t *sem);

    释放资源

    int sem_post(sem_t *sem);

    销毁信号量

    int sem_destroy(sem_t *sem);

    一个常见的面试题

    编写一个程序,开启3个线程,线程1输出A,线程2输出B,线程3输出C,要求输出结果必须按ABC的顺序显示;如:ABCABC….

    典型的线程同步的问题:

    线程1进行后线程2才能进行,然后才是线程3,线程3执行后线程1有开始执行。

    也就是:

    可以看到形成了一个环形,也就可能会因为出现环路等待而形成死锁,解决的办法就是,指定一个进程先执行,而且题目中让我们依次输出ABC,所以我们指定线程1先运行。

    代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <semaphore.h>
    
    static sem_t A_B;
    static sem_t B_C;
    static sem_t C_A;
    
    
    void *printA(void *arg)
    {
        int i = 0;
        for(i = 1;i < 11;i++)
        {
            sem_wait(&C_A);
            printf("第%02d次:A",i);
            sem_post(&A_B);
    
        }
        return NULL;
    }
    void  *printB(void *arg)
    {
        int i = 0;
        for(i = 1;i < 11;i++)
        {
            sem_wait(&A_B);
            printf("B");
            sem_post(&B_C);
    
        }
        return NULL;
    }
    void *printC(void *arg)
    {
        int i = 0;
        for(i = 1;i < 11;i++)
        {
            sem_wait(&B_C);
            printf("C
    ");
            sem_post(&C_A);
    
        }
        return NULL;
    }
    int main()
    {
        pthread_t thread_A;
        pthread_t thread_B;
        pthread_t thread_C;
        sem_init(&A_B,0,0);
        sem_init(&B_C,0,0);
        sem_init(&C_A,0,1);
        pthread_create(&thread_A,NULL,printA,NULL);
        pthread_create(&thread_B,NULL,printB,NULL);
        pthread_create(&thread_C,NULL,printC,NULL);
        pthread_join(thread_A,NULL);
        pthread_join(thread_B,NULL);
        pthread_join(thread_C,NULL);
        sem_destroy(&A_B);
        sem_destroy(&B_C);
        sem_destroy(&C_A);
        printf("
    ");
        
        return 0;
    
    }

    生产者消费者问题

    生产者消费者之间存在的互斥和同步关系分析

    首先,同一时间,只允许一个生产者对当前该生产的位置进行访问,所以生产者与生产者之间是互斥关系。

    再者,同一时间,只允许一个消费者对当前该消费的位置进行消费,所以消费者与消费者之间也是互斥关系

    最后,同一个位置要先生产再消费,所以生产者和消费者之间是同步关系。

    我在具体是现实,用了一个指针in表示下一个待生产的位置,由于生产者和消费者线程都需要访问这个指针in,所以in是一个临界区,生产者和消费者要互斥地访问,为了变成简便我直接也把生产者和消费者看成是互斥关系,但是这也导致临界区的粒度变大。

    代码如下:

    #include <stdio.h>
    #include <pthread.h>
    #include <semaphore.h>
    #include <unistd.h>
    #define NUM 10
    #define P_NUM 5
    #define C_NUM 10
    
    int buffer[10];
    int *in;
    
    pthread_mutex_t mutex;//buffer临界区
    
    sem_t p_sem;//消费者信号量
    sem_t c_sem;//生产者信号量
    
    
    
    
    void *producer(void *arg)
    {
        while(1)
        {
            sem_wait(&p_sem);
    
            pthread_mutex_lock(&mutex);
            printf("生产者生产了第%02d个位置的商品
    ",*in);
            in++;
            pthread_mutex_unlock(&mutex);
    
            sem_post(&c_sem);
            sleep(2);
        }
        return NULL;
    }
    void *consumer(void *arg)
    {
        while(1)
        {
            sem_wait(&c_sem);
    
            pthread_mutex_lock(&mutex);
            in--;
            printf("消费者消耗了第%02d个位置的商品
    ",*in);
            pthread_mutex_unlock(&mutex);
    
            sem_post(&p_sem);
            sleep(2);
        }
        return NULL;
    }
    
    int main()
    {
        int i = 1;
        for(i = 0;i < NUM;i++)
        {
            buffer[i] = i + 1;
        }
    
        in = buffer;
    
        //初始化互斥体
        pthread_mutex_init(&mutex,NULL);
        
        //初始化信号量
        sem_init(&p_sem,0,NUM);
        sem_init(&c_sem,0,0);
    
        pthread_t ppt[P_NUM];
    
        //创建生产者线程
        for(i = 0;i < P_NUM;i++)
        {
            pthread_create(&ppt[i],NULL,producer,NULL);
        }
        //创建消费者线程
        pthread_t cpt[C_NUM];
        for(i = 0;i < C_NUM;i++)
        {
            pthread_create(&cpt[i],NULL,consumer,NULL);
        }
        
        //回收资源
        for(i = 0;i < P_NUM;i++)
        {
            pthread_join(ppt[i],NULL);
        }
        for(i = 0;i < C_NUM;i++)
        {
            pthread_join(cpt[i],NULL);
        }
        pthread_mutex_destroy(&mutex);
        sem_destroy(&p_sem);
        sem_destroy(&c_sem);
    
        return 0;
    }

    读者写者问题

    读者写者也是一个非常著名的同步问题。读者写者问题描述非常简单,有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者在读文件时写者也不去能写文件。

    读者和写者之间存在互斥关系

    写者和写者之间存在互斥关系

    读者和读者之间没有互斥关系,是共享关系。

    这个问题的解决分为读者优先和写者优先:

    读者优先是这样的:一旦有读者成功访问,那么写者将被阻塞,允许后续读者,直到没有读者后,写者才被允许访问。

    写者优先是这样的:一旦后写者申请访问,那么将阻止后续读者继续访问,等当前读者读完后,写者开始写。

    读者优先代码如下:

    /**
     * 读者写者问题
     * 读者优先
     * 青儿哥哥
     * 博客园
     * 2017-09-27
     * */
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    
    #define R_NUM 10 //读者个数
    #define W_NUM 3 //写者个数
    
    pthread_t rpt[R_NUM];//读者线程的id
    pthread_t wpt[W_NUM];//写者线程的id
    
    int readercnt = 0;
    
    
    int buffer = 100;
    
    pthread_mutex_t buffer_mutex;//缓冲区的临界区
    
    pthread_mutex_t readercnt_mutex;//读者数量的临界区
    
    
    
    
    void write()
    {
        int rd = rand()%100;
        buffer = rd;
        printf("写者写:%d
    ",buffer);
    }
    void read()
    {
        printf("读者读:%d
    ",buffer);
    }
    
    void *reader(void *arg)
    {
        while(1)
        {
            pthread_mutex_lock(&readercnt_mutex);
            readercnt++;
            if(readercnt == 1)
            {
                pthread_mutex_lock(&buffer_mutex);
            }
            pthread_mutex_unlock(&readercnt_mutex);
    
            read();
    
            pthread_mutex_lock(&readercnt_mutex);
            readercnt--;
            if(readercnt == 0)
            {
                pthread_mutex_unlock(&buffer_mutex);
            }
            pthread_mutex_unlock(&readercnt_mutex);
            sleep(1);
    
        }
        return NULL;
        
    }
    void *writer(void *arg)
    {
        while(1)
        {
            pthread_mutex_lock(&buffer_mutex);
            write();
            pthread_mutex_unlock(&buffer_mutex);
            sleep(1);
        }
        return NULL;
    }
    
    int main()
    {
        //初始化互斥体
        pthread_mutex_init(&buffer_mutex,NULL);
        pthread_mutex_init(&readercnt_mutex,NULL);
    
        int i = 0;
        for(i = 0;i < R_NUM;i++)
        {
            pthread_create(&rpt[i],NULL,reader,NULL);
        }
        for(i = 0;i < W_NUM;i++)
        {
            pthread_create(&wpt[i],NULL,writer,NULL);
        }
    
        for(i = 0;i < R_NUM;i++)
        {
            pthread_join(rpt[i],NULL);
        }
        for(i = 0;i < W_NUM;i++)
        {
            pthread_join(wpt[i],NULL);
        }
    
        pthread_mutex_destroy(&buffer_mutex);
        pthread_mutex_destroy(&readercnt_mutex);
        
        
        return 0;
    }

    写者优先代码如下:

    /**
     * 读者写者问题
     * 写者优先
     * 青儿哥哥
     * 博客园
     * 2017-09-27
     * */
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    #include<semaphore.h>
    
    #define R_NUM 10 //读者个数
    #define W_NUM 3 //写者个数
    
    pthread_t rpt[R_NUM];//读者线程的id
    pthread_t wpt[W_NUM];//写者线程的id
    
    int readercnt = 0;
    int writercnt = 0;
    
    
    int buffer = 100;
    
    pthread_mutex_t buffer_mutex;//缓冲区的临界区
    
    pthread_mutex_t readercnt_mutex;//读者数量的临界区
    pthread_mutex_t writercnt_mutex;//写者数量的临界区
    
    sem_t reader_sem;//读者的信号量
    
    
    
    
    
    void write()
    {
        int rd = rand()%100;
        buffer = rd;
        printf("写者写:%d
    ",buffer);
    }
    void read()
    {
        printf("读者读:%d
    ",buffer);
    }
    
    void *reader(void *arg)
    {
        while(1)
        {
            sem_wait(&reader_sem);
            sem_post(&reader_sem);
            pthread_mutex_lock(&readercnt_mutex);
            readercnt++;
            if(readercnt == 1)
            {
                pthread_mutex_lock(&buffer_mutex);
            }
            pthread_mutex_unlock(&readercnt_mutex);
    
            read();
    
            pthread_mutex_lock(&readercnt_mutex);
            readercnt--;
            if(readercnt == 0)
            {
                pthread_mutex_unlock(&buffer_mutex);
            }
            pthread_mutex_unlock(&readercnt_mutex);
            sleep(1);
    
        }
        return NULL;
        
    }
    void *writer(void *arg)
    {
        while(1)
        {
            
            pthread_mutex_lock(&writercnt_mutex);
            writercnt++;
            if(writercnt == 1)
            {
                sem_wait(&reader_sem);
            }
            pthread_mutex_unlock(&writercnt_mutex);
            pthread_mutex_lock(&buffer_mutex);
            write();
            pthread_mutex_unlock(&buffer_mutex);
            pthread_mutex_lock(&writercnt_mutex);
            writercnt--;
            if(writercnt == 0)
            {
                sem_post(&reader_sem);
            }
            pthread_mutex_unlock(&writercnt_mutex);
    
            sleep(1);
        }
        return NULL;
    }
    
    int main()
    {
        //初始化互斥体
        pthread_mutex_init(&buffer_mutex,NULL);
        pthread_mutex_init(&readercnt_mutex,NULL);
        pthread_mutex_init(&writercnt_mutex,NULL);
    
        sem_init(&reader_sem,0,1);
    
        int i = 0;
        for(i = 0;i < R_NUM;i++)
        {
            pthread_create(&rpt[i],NULL,reader,NULL);
        }
        for(i = 0;i < W_NUM;i++)
        {
            pthread_create(&wpt[i],NULL,writer,NULL);
        }
    
        for(i = 0;i < R_NUM;i++)
        {
            pthread_join(rpt[i],NULL);
        }
        pthread_mutex_destroy(&buffer_mutex);
        pthread_mutex_destroy(&readercnt_mutex);
        pthread_mutex_destroy(&writercnt_mutex);
        sem_destroy(&reader_sem);
        
        
        return 0;
    
    }

    如果你觉得对你有用,请赞一个吧~~

  • 相关阅读:
    源码阅读-logback的StaticLoggerBinder如何提供ILoggerFactory的实现类
    源码阅读-logback解析之对接日志门面slf4j
    不可变对象 -final-unmodifiableX
    安全发布对象
    线程安全性-原子性-可见性-有序性
    并发相关基础知识
    并发与高并发介绍
    Spring源码解析-ioc容器的设计
    微服务架构概述
    获取当前时间到毫秒
  • 原文地址:https://www.cnblogs.com/qingergege/p/7605332.html
Copyright © 2011-2022 走看看