linux同步和互斥综合使用---生产者和消费者

在前面博客linux互斥锁和PV原语, linux信号量—互斥与同步
已经讲了PV原语、互斥锁、信号量,这一节讲他们的综合使用。用一个大家经常用的生产者和消费者的例子来讲解。

/* product_consumer.c*/

#include 
#include 
#include 
#include 
#include 

#include 

sem_t mutex,full_readable,empty_writable ;

static int count = 0 ;
#define FIFO "myfifo"
/*线程一*/
void thread1(void * arg)
{
    char write_buf[] = "1";
    int fd = *(int *)arg;
    printf("this is pthread1,fd = %d\n",fd);
    int i = 0;
    while(1)
    {
        /*P 操作信号量 empty_writable 和 mutex*/
        sem_wait(&empty_writable);
        sem_wait(&mutex);
        if (write(fd,write_buf,1))
        {
            perror("write");
        }
        count++;
        printf("This is a pthread1....and count = %d ,write 1 to the FIFO\n",count);
        /*V 操作信号量 full_readable 和 mutex*/
        sem_post(&full_readable);
        sem_post(&mutex);
        //sleep(1);
    }
}
/*线程二*/
void thread2(void * arg)
{
    char read_buf[6];
    int fd = *(int *)arg;
    printf("this is pthread2 and fd = %d\n",fd);
    int i = 0;
    int ret;
    while(1)
    {

        /*P 操作信号量 full 和 mutex*/
        sem_wait(&full_readable);
        sem_wait(&mutex);
        ret = read(fd,read_buf,1);
        if(ret < 0)
        {
            perror("read");
        }
        else if(ret > 0)
        {
            count++;
            printf("This is a pthread2.... count = %d, read %s from FIFO\n",count,read_buf);
        }
        /*V 操作信号量 avail 和 mutex*/
        sem_post(&empty_writable);
        sem_post(&mutex);
        sleep(3);
    }
}

int main(void)
{
    /*创建有名管道*/
    //if(mkfifo(FIFO,O_CREAT|O_EXCL)<0)
    //  printf("cannot create fifoserver\n");
    if(mkfifo(FIFO,O_CREAT|O_EXCL|777)<0)
        perror("cannot create fifoserver\n");
    /*打开管道*/
    int fd=open(FIFO,O_RDWR|O_NONBLOCK,0);
    if(fd==-1)
    {
        perror("open");
    }

    int i,ret;

    /*初始化互斥信号量为 1*/
    ret=sem_init(&mutex,0,1);
    /*初始化 empty_writable 信号量为 5,限制可写长度为5*/
    ret=sem_init(&empty_writable,0,5);
    /*初始化 full_readable 信号量为 0*/
    ret=sem_init(&full_readable,0,0);

    if(ret!=0)
    {
        perror("sem_init");
    }

    pthread_t id1,id2;

    /*创建线程一*/
    ret=pthread_create(&id1,NULL,(void *) thread1,&fd);
    if(ret!=0){
    perror("Create pthread error!\n");
    }
    /*创建线程二*/
    ret=pthread_create(&id2,NULL,(void *) thread2,&fd);
    if(ret!=0){
    perror ("Create pthread error!\n");
    }

    /*等待线程结束*/
    pthread_join(id1,NULL);
    pthread_join(id2,NULL);
    return 0;
}

实验结果:

ubuntu:~/test/pthread_test$ gcc product_consumer.c -o product_consumer -lpthread
ubuntu:~/test/pthread_test$ ./product_consumer
this is pthread1,fd = 3
this is pthread2 and fd = 3
write: Success
This is a pthread1....and count = 1 ,write 1 to the FIFO
write: Success
This is a pthread1....and count = 2 ,write 1 to the FIFO
write: Success
This is a pthread1....and count = 3 ,write 1 to the FIFO
write: Success
This is a pthread1....and count = 4 ,write 1 to the FIFO
write: Success
This is a pthread1....and count = 5 ,write 1 to the FIFO
This is a pthread2.... count = 6, read 1 from FIFO
write: Success
This is a pthread1....and count = 7 ,write 1 to the FIFO
This is a pthread2.... count = 8, read 1 from FIFO

以上程序,我们用PV原语来理解。
一开始empty_writable初始化5,表示管道有5个空位可以写。
一开始full_readable初始化为0,表示管道有0个有写的数据可读。
当线程一写了两个空位的时候,empty_writable变成了3,表示还有3个空位可写。而full_readable变成了2,表示有两个位置有数据可读。我们很容易想到下面的同步操作模式。

/*线程一*/
void thread1(void * arg)
{
    sem_wait(&empty_writable);
    //write 操作
    sem_post(&full_readable);
}
/*线程二*/
void thread2(void * arg)
{
    sem_wait(&full_readable);
        //read 操作
    sem_post(&empty_writable);
}

但是,这里有一个问题,就是当empty_writable为3,full_readable为2的时候,线程一和线程二都可以访问同一个管道,这时候就容易出现问题了。所以,我们这里还需要加上一把互斥锁,用于防止进程共享的资源被同时访问。

/*线程一*/
void thread1(void * arg)
{
    sem_wait(&empty_writable);
    sem_wait(&mutex);
    //write 管道
    sem_post(&full_readable);
    sem_post(&mutex);
}
/*线程二*/
void thread2(void * arg)
{
    sem_wait(&full_readable);
    sem_wait(&mutex);
        //read 管道
    sem_post(&empty_writable);
    sem_post(&mutex);
}

程序的功能很简单,首先在main函数中调用mkfifo(FIFO,O_CREAT|O_EXCL|777)创建管道,调用open函数打开管道;接着再创建线程一和线程二,在线程一中对管道写入数据,在线程二中读取管道的数据。从上面的试验结果可知道,线程一对管道写入5次数据之后,就无法再写入了,这是因为我们把信号量empty_writable 初始化为 5。所以,线程一开始阻塞在sem_wait(&empty_writable),然后线程二开始读取数据,读取一次,就空一个位置出来,线程一就可以再写入一次数据。

你可能感兴趣的:(linux系统编程)