首页 > 代码库 > Linux环境编程之同步(一):互斥锁

Linux环境编程之同步(一):互斥锁

同步的内容在《UNP2》里把它看作了进程间通信,我觉得其实同步只是进程间通信的一种协作方式一种协作的手段,不能称之为进程间通信的一种形式,所以标题用了“同步”,而没有用IPC进程间通信。

互斥锁是同步的基本组成部分,它们总是用来同步一个进程内的各个线程的。如果互斥锁或条件变量存放在多个进程间共享的某个内存区,那么Posix还允许它用于这些进程间的同步。

互斥锁用于保护临界区以保证任何时刻只有一个线程在执行其中的代码,或者任何时刻只有一个进程在执行其中的代码。保护临界区的代码形式一般如下:

lock_the_mutex();
临界区
unlock_the_mutex();

既然任何时刻只有一个线程能够锁住一个给定的互斥锁,于是这样的代码保证任何时刻只有一个线程在执行其临界区中的指令。

Posix互斥锁被声明为具有pthread_mutex_t数据类型的变量。如果互斥锁变量是静态分配的,可把它初始化为常值PTHREAD_MUTEX_INITIALIZER,例如:

static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

如果互斥锁是动态分配的,或者分配在共享内存区中,就必须在运行之时通过调用pthread_mutex_init函数来初始化它。

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mptr);
int pthread_mutex_trylock(pthread_mutex_t *mptr);
int pthread_mutex_unlock(pthread_mutex_t *mptr);
如果尝试给一个已由另外某个线程锁住的互斥锁上锁,那么pthread_mutex_lock将阻塞到该互斥锁解锁为止。pthread_mutex_trylock是对应的非阻塞函数,如果互斥锁已锁住,它就返回一个EBUSY错误。互斥锁保护多个线程或多个进程共享的临界区的的共享数据。


生产者消费者问题是同步中的一个经典问题:一个或多个生产者(线程或进程)创建一个个的数据条目,然后这些条目由一个或多个消费者(线程或进程)处理。数据条目在生产者和消费者之间使用某种类型的IPC传递的。

同步分为显式同步和隐式同步。shell中的管道问题就是一个生产者消费者问题,如grep pattern chapters.*|wc -l。这种类型的同步是隐式的,即生产者和消费者不知道内核在执行同步。如果用消息队列作为生产者消费者间的IPC形式,内核仍然会处理同步,即同步仍然是隐式的。然而当共享内存区用作生产者和消费者之间的IPC形式时,生产者和消费者必须执行某种类型的显式同步,互斥锁就是显式同步。
以等待方式实现生产者消费者问题程序如下:

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

#define MAXNITEMS 	1000000
#define MAXNTHREADS     100

int	nitems;		/*read-only by producer and consumer*/
struct{
	pthread_mutex_t	mutex;
	int				buff[MAXNITEMS];
	int 			nput;
	int				nval;
}shared={
	PTHREAD_MUTEX_INITIALIZER
};

void *produce(void*), *consume(void*);

int min(int a, int b)
{
	return((a < b) ? a : b);
}

int
main(int argc, char **argv)
{
	int 	i, nthreads, count[MAXNTHREADS];
	pthread_t	tid_produce[MAXNTHREADS], tid_consume;

	if(argc != 3){
		printf("usage:produce <#items> <#threads>.\n");
		return -1;
	}
	nitems = min(atoi(argv[1]), MAXNITEMS);
	nthreads = min(atoi(argv[2]), MAXNTHREADS);

	pthread_setconcurrency(nthreads);

	/*start all the producer threads*/
	for(i = 0; i < nthreads; i++){
		count[i] = 0;
		pthread_create(&tid_produce[i], NULL, produce, &count[i]);	
	}

	/*wait for all the producer threads*/
	for(i = 0; i < nthreads; i++){
		pthread_join(tid_produce[i], NULL);	
		printf("count[%d] = %d\n", i, count[i]);
	}

	/*start, then wait for the consumer thread*/
	pthread_create(&tid_consume, NULL, consume, NULL);
	pthread_join(tid_consume, NULL);


	exit(0);
}

void *
produce(void *arg)
{
	for(;;){
		pthread_mutex_lock(&shared.mutex);
		if(shared.nput >= nitems){
			pthread_mutex_unlock(&shared.mutex);
			return (NULL);			/*array if full, we're done*/	
		}
		shared.buff[shared.nput] = shared.nval;
		shared.nput++;
		shared.nval++;
		pthread_mutex_unlock(&shared.mutex);
		*((int *)arg) += 1;
	}
}
互斥锁用于等待而不用于等待的程序如下:

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

#define MAXNITEMS 	1000000
#define MAXNTHREADS     100

int	nitems;		/*read-only by producer and consumer*/
struct{
	pthread_mutex_t	mutex;
	int				buff[MAXNITEMS];
	int 			nput;
	int				nval;
}shared={
	PTHREAD_MUTEX_INITIALIZER
};

void *produce(void*), *consume(void*);

int min(int a, int b)
{
	return((a < b) ? a : b);
}

int
main(int argc, char **argv)
{
	int 	i, nthreads, count[MAXNTHREADS];
	pthread_t	tid_produce[MAXNTHREADS], tid_consume;

	if(argc != 3){
		printf("usage:produce <#items> <#threads>.\n");
		return -1;
	}
	nitems = min(atoi(argv[1]), MAXNITEMS);
	nthreads = min(atoi(argv[2]), MAXNTHREADS);

	/*create all producers and one consumer*/
	pthread_setconcurrency(nthreads + 1);

	/*start all the producer threads*/
	for(i = 0; i < nthreads; i++){
		count[i] = 0;
		pthread_create(&tid_produce[i], NULL, produce, &count[i]);	
	}
	pthread_create(&tid_consume, NULL, consume, NULL);

	/*wait for all the producer and the consumer*/
	for(i = 0; i < nthreads; i++){
		pthread_join(tid_produce[i], NULL);	
		printf("count[%d] = %d\n", i, count[i]);
	}
	pthread_join(tid_consume, NULL);

	exit(0);
}

void *
produce(void *arg)
{
	for(;;){
		pthread_mutex_lock(&shared.mutex);
		if(shared.nput >= nitems){
			pthread_mutex_unlock(&shared.mutex);
			return (NULL);			/*array if full, we're done*/	
		}
		shared.buff[shared.nput] = shared.nval;
		shared.nput++;
		shared.nval++;
		pthread_mutex_unlock(&shared.mutex);
		*((int *)arg) += 1;
	}
}

void 
consume_wait(int i)
{
	for(;;){
		pthread_mutex_lock(&shared.mutex);	
		if(i < shared.nput){
			pthread_mutex_unlock(&shared.mutex);	
			return;				/*an item is already*/
		}
		pthread_mutex_unlock(&shared.mutex);
	}
}

void *
consume(void *arg)
{
	int i;

	for(i = 0; i < nitems; i++){
		consume_wait(i);
		if(shared.buff[i] != i){
			printf("buff[%d] = %d\n", i, shared.buff[i]);	
		}	
	}
	return NULL;
}