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\n");
        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("\n");
    
    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个位置的商品\n",*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个位置的商品\n",*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\n",buffer);
}
void read()
{
    printf("读者读:%d\n",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\n",buffer);
}
void read()
{
    printf("读者读:%d\n",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;

}

 

 

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

转载于:https://www.cnblogs.com/qingergege/p/7605332.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值