下面介绍线程的三种同步机制:
1、互斥量pthread_mutex_t
互斥量就是锁,对某段临界区进行加锁,使得线程只能串行访问这段临界区。对互斥量进行加锁后,任何其它试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥量。
2、读写锁pthread_rwlock_t
与互斥量类似,但并行性更高。读写锁有三种状态:
读模式加锁状态
写模式加锁状态
不加锁状态
一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。下面是读写锁的逻辑关系表:
锁状态\需要加的锁
读模式加锁
写模式加锁
读模式
允许
阻塞直到所有线程释放读锁
写模式
阻塞直到写锁被释放
阻塞直到写锁被释放
不加锁
允许
允许
3、条件变量pthread_cond_t
条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定条件的发生。条件变量(由用户自由定义)本身受互斥量保护,对条件的修改都必须先加锁。
这里要注意条件和条件变量之间的区别。条件变量是由系统提供的结构体pthread_cond_t,它用来在多个线程间传递信号;而条件则由用户代码定义,用于多个线程间共享某些数据。
测试代码:
#include
#include
/* 自定义的条件 */
struct msg {
struct msg *m_next;
char *data;
};
struct msg *workq; /* 定义一个消息队列 */
pthread_cond_t qready = PTHREAD_COND_INITIALIZER; /* 静态分配条件变量 */
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER; /* 静态分配互斥量 */
void process_msg(void)
{
struct msg *mp;
while (1)
{
pthread_mutex_lock(&qlock);
while (workq == NULL)
{
/* 此函数把线程放到等待条件的线程列表上,然后对互斥量解锁
* 函数返回时,互斥量再次被锁住
*/
pthread_cond_wait(&qready, &qlock);
}
/* 从消息队列头取出一个消息 */
mp = workq;
workq = workq->m_next;
pthread_mutex_unlock(&qlock);
printf("%s\n", mp->data); /* 打印收到的消息 */
}
}
void enqueue_msg(struct msg *mp)
{
pthread_mutex_lock(&qlock);
mp->m_next = workq; /* 新消息放队列头部 */
workq = mp;
pthread_mutex_unlock(&qlock);
pthread_cond_signal(&qready); /* 唤醒等待该条件的某个线程 */
}
void *thr_fn1(void *arg)
{
process_msg();
}
void *thr_fn2(void *arg)
{
struct msg msg1, msg2, msg3;
msg1.data = "Hello world";
msg2.data = "I love you";
msg3.data = "This is a test!";
/* 将消息放入队列 */
enqueue_msg(&msg1);
enqueue_msg(&msg2);
enqueue_msg(&msg3);
}
int main(void)
{
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, thr_fn1, NULL);
pthread_create(&tid2, NULL, thr_fn2, NULL);
pthread_join(tid1, NULL);
return 0;
}
运行结果: