Linux系统编程之互斥量和条件变量实现生产者消费者问题

一、 互斥量

当多个线程共享一个变量时,一个线程读取这个变量的值,而有另外一个线程会修改这个变量的值,如果不加限制,就难以保证读取的变量值是修改之后还是修改之前的。为了保证变量不会被多个线程同时访问,引入互斥量,互斥量对共享数据的保护就像一把锁。

在pthreads中,任何时候仅有一个线程可以锁定互斥量,因此,当多个线程尝试去锁定互斥量时仅有一个会成功,直到锁定互斥量的线程解锁互斥量后,其他线程才可以去锁定。如此,线程就会轮流访问受保护数据。这就是互斥量的概念,在pthread中定义了一套用于线程同步的mutex函数。

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
    const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// pthread_mutex_destroy, pthread_mutex_init — destroy and initialize a mutex
#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock — lock and unlock a mutex

二、 条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待“条件变量的条件成立”而挂起;另一个线程使“条件成立”(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥量结合在一起。

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond,
    const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// pthread_cond_destroy, pthread_cond_init — destroy and initialize condition variables
#include <pthread.h>

int pthread_cond_timedwait(pthread_cond_t *restrict cond,
    pthread_mutex_t *restrict mutex,
    const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
    pthread_mutex_t *restrict mutex);
// pthread_cond_timedwait, pthread_cond_wait — wait on a condition
#include <pthread.h>

int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
// pthread_cond_broadcast, pthread_cond_signal — broadcast or signal a condition

三、 生产者消费者问题

1、 示例代码

#include <stdio.h>
#include <pthread.h>

#define MAX_VALUE 2
#define MAX_COUNT 5

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;

int value = 0;

void *producer(void *arg)
{
	for(int i=0; i<MAX_COUNT; i++)
	{
		pthread_mutex_lock(&mutex);

		while(value == MAX_VALUE)
		{
			printf("Producer: waiting, full!\n");
			pthread_cond_wait(&not_full, &mutex);
		}

		value++;
		printf("Producer: value == %d\n", value);
		
		pthread_cond_signal(&not_empty);
		pthread_mutex_unlock(&mutex);
	}
	return NULL;
}

void *consumer(void *arg)
{
	for(int i=0; i<MAX_COUNT; i++)
	{
		pthread_mutex_lock(&mutex);

		while(value == 0)
		{
			printf("Consumer: waiting, empty!\n");
			pthread_cond_wait(&not_empty, &mutex);
		}

		printf("Consumer: value == %d\n", value);
		value--;

		pthread_cond_signal(&not_full);
		pthread_mutex_unlock(&mutex);
	}
	return NULL;
}

int main(void)
{
	pthread_t t1, t2, t3, t4;

	pthread_create(&t1, NULL, producer, NULL);
	pthread_create(&t2, NULL, producer, NULL);
	pthread_create(&t3, NULL, consumer, NULL);
	pthread_create(&t4, NULL, consumer, NULL);

	pthread_join(t1, NULL);
	pthread_join(t2, NULL);
	pthread_join(t3, NULL);
	pthread_join(t4, NULL);

	return 0;
}

2、 运行结果

[ycxie@fedora Workspace]$ gcc mutex_cond.c -o mutex_cond -pthread -Wall
[ycxie@fedora Workspace]$ ./mutex_cond
Producer: value == 1
Producer: value == 2
Producer: waiting, full!
Consumer: value == 2
Consumer: value == 1
Consumer: waiting, empty!
Consumer: waiting, empty!
Producer: value == 1
Producer: value == 2
Consumer: value == 2
Consumer: value == 1
Consumer: waiting, empty!
Consumer: waiting, empty!
Producer: value == 1
Producer: value == 2
Producer: waiting, full!
Consumer: value == 2
Consumer: value == 1
Consumer: waiting, empty!
Consumer: waiting, empty!
Producer: value == 1
Producer: value == 2
Producer: waiting, full!
Producer: waiting, full!
Consumer: value == 2
Producer: value == 2
Consumer: value == 2
Consumer: value == 1
Consumer: waiting, empty!
Producer: value == 1
Consumer: value == 1

Leave a Reply

Your email address will not be published. Required fields are marked *