unix进程及线程之间的通信:互斥锁和条件变量

25 篇文章 0 订阅

进程或线程间的通信,通常是为了实现各进程或线程之间的同步,我们的讨论主要分为以下几个方面:

1.消息传递(管道(匿名管道),FIFO(有名管道),消息队列)

2.同步(互斥锁,读写锁,条件变量,信号量)

3.共享内存区

本节我们集中讨论互斥锁,条件变量来实现进程跟线程之间的同步。

 

线程间的同步:互斥锁实现互斥访问临界资源

初始化和反初始化:

       #include <pthread.h>

       pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //静态分配的互斥锁的初始化;互斥锁用pthread_mutex_t类型表示
       int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);//用互斥锁属性初始化,默认属性为NULL
       int pthread_mutex_destroy(pthread_mutex_t *mutex);

上锁与解锁:

       int pthread_mutex_lock(pthread_mutex_t *mutex); //上锁,如果未获得锁,则阻塞
       int pthread_mutex_trylock(pthread_mutex_t *mutex); //上锁,如果未获得锁,不阻塞,直接返回EBUSY
       int pthread_mutex_unlock(pthread_mutex_t *mutex); 
       int pthread_mutex_timedlock(pthread_mutex_t *mutex,const struct timespec *abs_timeout); //如果未获得锁,则阻塞到abs_timeout后,返回超时错误,ETIMEDOUT,若在abs_timeout时刻之前获得了锁,则成功返回,加锁成功。

/* include main */
#include	"unpipc.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
main(int argc, char **argv)
{
	int			i, nthreads, count[MAXNTHREADS];
	pthread_t	tid_produce[MAXNTHREADS], tid_consume;

	if (argc != 3)
		err_quit("usage: prodcons2 <#items> <#threads>");
	nitems = min(atoi(argv[1]), MAXNITEMS);
	nthreads = min(atoi(argv[2]), MAXNTHREADS);

	Set_concurrency(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);
}
/* end main */

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

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

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

上面是使用互斥锁实现的多个生产者,一个消费者的例子,生产者往buff中填入数据,消费者取数据比较填入数据是否正确。为了简化逻辑,我们让生产者先执行,他们互斥的访问公共内存buff,以及数组下标nput.当生产者生产完数据后,我们再执行消费者线程。我们也可以同时执行生产者跟消费者线程,那样的话就需要一种机制来同步生产者跟消费者线程,这就是即将介绍的条件变量。

 

线程间的同步:条件变量实现线程之间的同步

       int pthread_cond_destroy(pthread_cond_t *cond); //反初始化
       int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr); //用属性初始化,默认属性为NULL
       pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //静态条件变量初始化
       int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex,const struct timespec *abstime);//线程阻塞一定的时间
       int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);//线程阻塞,直到其他线程调用cond_signal唤醒之
       int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒cond_wait阻塞的所有线程线程
       int pthread_cond_signal(pthread_cond_t *cond);//唤醒cond_wait阻塞的一个线程

总的来说,条件变量进行线程同步的代码如下:

struct{
        pthread_mutex_t mutex;
        pthread_cond_t  cond;
        /*其他维护本条件的各个变量*/
}var={PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER,/*其他维护本条件的各个变量的初始化*/};


//在线程1中,条件发生,设置条件为真
pthread_mutex_lock(&var.mutex);
设置条件为真
pthread_cond_signal(&var.cond);
pthread_mutex_unlock(&var.mutex);

//在线程2中,线程阻塞,在while循环中等待条件的发生
pthread_mutex_lock(&var.mutex);
while(条件为假)
        pthread_cond_wait(&var.cond,&var.mutex);
修改条件
pthread_mutex_unlock(&var.mutex);

现在我们用条件变量修改生产者-消费者程序的代码,这一次让消费者跟生产者一起运行,并用条件变量来进行同步:

#include	"unpipc.h"

#define	MAXNITEMS 		1000000
#define	MAXNTHREADS			100

/* globals shared by threads */
int		nitems;				/* read-only by producer and consumer */
int		buff[MAXNITEMS];
struct {
  pthread_mutex_t	mutex;
  int				nput;	/* next index to store */
  int				nval;	/* next value to store */
} put = { PTHREAD_MUTEX_INITIALIZER };

struct {
  pthread_mutex_t	mutex;
  pthread_cond_t	cond;
  int				nready;	/* number ready for consumer */
} nready = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER };
/* end globals */

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

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

	if (argc != 3)
		err_quit("usage: prodcons6 <#items> <#threads>");
	nitems = min(atoi(argv[1]), MAXNITEMS);
	nthreads = min(atoi(argv[2]), MAXNTHREADS);

	Set_concurrency(nthreads + 1);
		/* 4create all producers and one consumer */
	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 producers 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);
}
/* end main */

/* include prodcons */
void *produce(void *arg)
{
	for ( ; ; ) {
		Pthread_mutex_lock(&put.mutex);
		if (put.nput >= nitems) {
			Pthread_mutex_unlock(&put.mutex);
			return(NULL);		/* array is full, we're done */
		}
		buff[put.nput] = put.nval;
		put.nput++;
		put.nval++;
		Pthread_mutex_unlock(&put.mutex);

		Pthread_mutex_lock(&nready.mutex);
		if (nready.nready == 0)
			Pthread_cond_signal(&nready.cond);
		nready.nready++;
		Pthread_mutex_unlock(&nready.mutex);

		*((int *) arg) += 1;
	}
}

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

	for (i = 0; i < nitems; i++) {
		Pthread_mutex_lock(&nready.mutex);
		while (nready.nready == 0)
			Pthread_cond_wait(&nready.cond, &nready.mutex);
		nready.nready--;
		Pthread_mutex_unlock(&nready.mutex);

		if (buff[i] != i)
			printf("buff[%d] = %d\n", i, buff[i]);
	}
	return(NULL);
}
/* end prodcons */

在上述程序中,我们用nready变量跟踪生产者已生产的数目,当生产者数目为0,表示buff数据为空,消费者调用pthread_cond_wait阻塞,当buff数据从0变为1时,表示已经生产了数据,生产者调用pthread_cond_sinnal唤醒消费者。

 

在本节的最后,我们给一个典型的tcp服务器程序的实现demo,我们的服务器程序在程序启动阶段就创建一个线程池,之后只让主线程调用accept,并把accept后的套接字描述符传给线程池中的某个线程,让该线程进行处理连接。

/* include serv08 */
#include	"unpthread.h"

typedef struct {
  pthread_t		thread_tid;		/* thread ID */
  long			thread_count;	/* # connections handled */
} Thread;
Thread	*tptr;		/* array of Thread structures; calloc'ed */

#define	MAXNCLI	32
int					clifd[MAXNCLI], iget, iput;

static int			nthreads;
pthread_mutex_t		clifd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t		clifd_cond = PTHREAD_COND_INITIALIZER;

//创建线程
void thread_make(int i)
{
	void	*thread_main(void *);

	Pthread_create(&tptr[i].thread_tid, NULL, &thread_main, (void *) i);
	return;		/* main thread returns */
}

//线程函数,处理连接
void *thread_main(void *arg)
{
	int		connfd;
	void	web_child(int);

	printf("thread %d starting\n", (int) arg);
	for ( ; ; ) {
    	Pthread_mutex_lock(&clifd_mutex);
		while (iget == iput)
			Pthread_cond_wait(&clifd_cond, &clifd_mutex);
		connfd = clifd[iget];	/* connected socket to service */
		if (++iget == MAXNCLI)
			iget = 0;
		Pthread_mutex_unlock(&clifd_mutex);
		tptr[(int) arg].thread_count++;

		web_child(connfd);		/* process request */
		Close(connfd);
	}
}

int main(int argc, char **argv)
{
	int			i, listenfd, connfd;
	void		sig_int(int), thread_make(int);
	socklen_t	addrlen, clilen;
	struct sockaddr	*cliaddr;

	if (argc == 3)
		listenfd = Tcp_listen(NULL, argv[1], &addrlen);
	else if (argc == 4)
		listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
	else
		err_quit("usage: serv08 [ <host> ] <port#> <#threads>");
	cliaddr = Malloc(addrlen);

	nthreads = atoi(argv[argc-1]);
	tptr = Calloc(nthreads, sizeof(Thread));
	iget = iput = 0;

		/* 4create all the threads */
	for (i = 0; i < nthreads; i++)
		thread_make(i);		/* only main thread returns */

	Signal(SIGINT, sig_int);

	for ( ; ; ) {
		clilen = addrlen;
		connfd = Accept(listenfd, cliaddr, &clilen);

		Pthread_mutex_lock(&clifd_mutex);
		clifd[iput] = connfd;
		if (++iput == MAXNCLI)
			iput = 0;
		if (iput == iget)
			err_quit("iput = iget = %d", iput);
		Pthread_cond_signal(&clifd_cond);
		Pthread_mutex_unlock(&clifd_mutex);
	}
}
/* end serv08 */

void sig_int(int signo)
{
	int		i;
	void	pr_cpu_time(void);

	pr_cpu_time();

	for (i = 0; i < nthreads; i++)
		printf("thread %d, %ld connections\n", i, tptr[i].thread_count);

	exit(0);
}





 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值