线程同步:
读写锁:
继续往下说线程同步问题。读写锁与互斥量类似,但是读写锁的好处是允许更高的并行性。读写锁有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。下面有中情况需要说明以下,写加锁状态时,未被解锁的时候,所有试图对这个锁进行加锁的线程都会被阻塞。读加锁状态时,所有以读模式对它进行加锁的线程都可以得到访问的权限,但是如果线程希望以写模式对此锁进行加锁时,必须阻塞到所有线程释放读锁。读写锁比较适合与对于数据结构读的次数远远大于写的情况。
与互斥量一样,读写锁在使用的时候也需要初始化,在释放他们底层的内存前必须销毁。
#include <pthread.h>
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 <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);<span style="white-space:pre"> </span>//读锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);<span style="white-space:pre"> </span>//写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);<span style="white-space:pre"> </span>//解锁
补充一下有条件的读写锁原语:
#include <pthread.h>
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 <stdlib.h>
#include <pthread.h>
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 <pthread.h>
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 <pthread.h>
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 <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);<span style="white-space:pre"> </span>//唤醒等待该条件的某个线程
int pthread_cond_broadcast(pthread_cond_t *cond);<span style="white-space:pre"> </span>//唤醒等待该条件的所有线程
下面的代码是结合条件变量和互斥量对线程进行同步。
/*
*Name : list11_9.c
*Author : Lniper
*Date : 2016-02-17
*Aim : The condition variable and mutex make the thread synchronization.
*/
#include <pthread.h>
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);
}
总结:
对于线程的同步的三种基本的机制:互斥,读写锁以及条件变量。对于线程的同步还需要多多的实践,编写程序才能更好的理解线程同步的精髓。