APUE之线程(五)

线程同步:

读写锁:

  继续往下说线程同步问题。读写锁与互斥量类似,但是读写锁的好处是允许更高的并行性。读写锁有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。下面有中情况需要说明以下,写加锁状态时,未被解锁的时候,所有试图对这个锁进行加锁的线程都会被阻塞。读加锁状态时,所有以读模式对它进行加锁的线程都可以得到访问的权限,但是如果线程希望以写模式对此锁进行加锁时,必须阻塞到所有线程释放读锁。读写锁比较适合与对于数据结构读的次数远远大于写的情况。

  与互斥量一样,读写锁在使用的时候也需要初始化,在释放他们底层的内存前必须销毁。

#include 
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destory(pthread_rwlock_t *rwlock);
   pthread_rwlock_init()进行初始化,释放读写锁占用的内存前,用pthread_rwlock_destory()做清理工作。

  下面就是三种锁的函数:

#include 
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);				//读锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);				//写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);				//解锁
   补充一下有条件的读写锁原语:
#include 
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
   下面的程序是使用读写锁的一个例子:
/*
 *Name : list11_8.c
 *Author : Lniper
 *Date : 2016-02-17
 *Aim : Using the write and read lock.
 */
#include 
#include 

struct job{
	struct job *j_next;
	struct job *j_prev;
	pthread_t j_id;			/* tells which thread handles this job */
	/* ... more stuff here ... */
};

struct queue{
	struct job *q_head;
	struct job *q_tail;
	pthread_rwlock_t q_lock;
};

/*
 *Initialize a queue.
 */
int queue_init(struct queue *qp)
{
	int err;
		
	qp->q_head = NULL;
	qp->q_tail = NULL;
	err = pthread_rwlock_init(&qp->q_lock, NULL);
	if(err != 0)
		return (err);
	
	/* ... continue initialization ... */
	
	return(0);
}

/*
 *Insert a job at the head of the queue.
 */
void job_insert(struct queue *qp, struct job *jp)
{
	pthread_rwlock_wrlock(&qp->q_lock);
	jp->j_next = qp->q_head;
	jp->j_prev = NULL;
	if(qp->q_head != NULL)
		qp->q_head->j_prev = jp;
	else 
		qp->q_tail = jp;			/* list was empty */
	qp->q_head = jp;
	
	pthread_rwlock_unlock(&qp->q_lock);
}

/*
 *Append a job on the tail of the queue.
 */
void job_append(struct queue *qp, struct job *jp)
{
	pthread_rwlock_wrlock(&qp->q_lock);
	jp->j_next = NULL;
	jp->j_prev = qp->q_tail;
	if(qp->q_tail != NULL)
		qp->q_tail->j_next = jp;
	else 
		qp->q_head = jp;			/* list was empty */
	qp->q_tail = jp;
	pthread_rwlock_unlock(&qp->q_lock);
}

/*
 *Remove the given job from a queue.
 */
void job_remove(struct queue *qp, struct job *jp)
{
	pthread_rwlock_wrlock(&qp->q_lock);
	if (jp == qp->q_head) {
		qp->q_head = jp->j_next;
		if(qp->q_tail == jp)
			qp->q_tail = NULL;
	} else if (jp == qp->q_tail) {
		qp->q_tail = jp->j_prev;
		if(qp->q_head == jp)
			qp->q_head = NULL;
	} else {
		jp->j_prev->j_next = jp->j_next;
		jp->j_next->j_prev = jp->j_prev;
	}
	pthread_rwlock_unlock(&qp->q_lock);
}

/* 
 *Find a job for the given thread ID.
 */
struct job *job_find(struct queue *qp, pthread_t id)
{
	struct job *qp;

	if(pthread_rwlock_rdlock(&qp->q_lock) != 0)
		return (NULL);
	
	for(jp = qp->q_head; jp != NULL; jp = jp->j_next)
		if(pthread_equal(jp->j_id, id))
			break;
	
	pthread_rwlock_unlock(&qp->q_lock);
	return (jp);
}
   这个例子中,无论什么时候需要增加一个作业到队列中或者从队列中删除作业,都用写模式锁住队列的读写锁。另外无论什么时候搜索队列,首先需要获取读模式下的锁,允许所有的工作线程并发的搜索队列。这种情况下如果想改善性能,只有线程搜索队列的频率远远高于增加或者删除作业的频率。

条件变量:

  这个是线程可用的另一种同步机制。条件变量给多个线程提供了一个会合的场所。条件变量和互斥量一起使用的时候,允许线程以无竞争的方式等待特定的条件发生。

#include 
int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
   pthread_cond_init()函数是动态分配的初始化函数,PTHREAD_COND_INITIALIZER分给静态分配的条件变量。pthread_cond_destroy()函数对条件变量进行去除初始化。
#include 
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);
  pthread_cond_timedwait()函数提供了timeout,timeout指定了等待的时间,通过timespec结构指定。时间用秒数或者分秒数来表示(纳秒)。

struct timespec {
	time_t	tv_sec;			/* seconds */
	long		tv_nsec;			/* nanoseconds */
};
   下面提供两个函数对于唤醒等待条件的某个或者所有的线程:

#include 
int pthread_cond_signal(pthread_cond_t *cond);				//唤醒等待该条件的某个线程
int pthread_cond_broadcast(pthread_cond_t *cond);			//唤醒等待该条件的所有线程

  下面的代码是结合条件变量和互斥量对线程进行同步。

/*
 *Name : list11_9.c
 *Author : Lniper 
 *Date : 2016-02-17
 *Aim : The condition variable and mutex make the thread synchronization.
 */
#include 
struct msg{
	struct msg *m_next;
	/* ... more stuff here ... */
};

structy msg *workq;
pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;

void process_msg(void)
{
	struct msg *mp;
	
	for (;;) {
		pthread_mutex_lock(&qlock);
		while (workq = NULL)
			pthread_cond_wait(&qready, &qlock);
		mp = workq;
		workq = mp->m_next;
		pthread_mutex_unlock(&qlock);
		/* new process the message mp */
	}
}
	
void enqueue_msg(struct msg *mp)
{
	pthread_mutex_lock(&qlock);
	mp->m_next = workq;
	workq = mp;
	pthread_mutex_unlock(&qlock);
	pthread_cond_signal(&qready);
}

总结:

  对于线程的同步的三种基本的机制:互斥,读写锁以及条件变量。对于线程的同步还需要多多的实践,编写程序才能更好的理解线程同步的精髓。









你可能感兴趣的:(环境编程_My,endeavor)