线程同步《UNIX 环境高级编程》

互斥量
可以通过使用Pthread的互斥接口保护数据,确保同一时间只有一个线程访问数据。互斥量(mutex)从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去再次等待它重新变为可用。在这种方式下,每次只有一个线程可以向前执行。

避免死锁
如果线程试图对同一个互斥量加锁两次,那么它自身就会陷入死锁状态,使用互斥量时,还有其他更不明显的方式也能产生死锁。例如,程序中使用多个互斥量时,如果允许一个线程一直占有一个互斥量,并且在试图锁住第二个互斥量时处于阻塞状态,但是拥有第二个互斥量的线程也在试图锁住第一个互斥量,这时就会发生死锁。因为两个线程都在相互请求另一个线程拥有的资源,所以这两个线程都无法运行,于是就产生死锁。
可以通过小心地控制互斥量加锁的顺序来避免死锁的发生。例如,假设需要对两个互斥量A和B同时加锁,如果所有线程总是在对互斥量B加锁之前锁住互斥量A,那么使用这两个互斥量不会产生死锁,类似地,如果所有的线程总是在锁住互斥量A之前锁住互斥量B,那么也不会发生死锁。只有在一个线程试图与另一个线程相反的顺序锁住互斥量时,才可能出现死锁。

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

条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。
条件变量使用之前必须首先进行初始化,可以把常量PTHREAD_COND_INTIALIZER赋给静态分配的条件变量,但是如果条件变量是动态分配的,可以使用pthread_cond_init函数进行初始化。
在释放底层的内存空间之前,可以使用Pthread_mutex_destroy函数对条件变量进行去除初始化。
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 函数的attr参数可以设置为NULL,使用pthread_cond_wait 等待条件变为真,如果在给定的时间内条件不能满足,那么会生成一个代表出错码的返回变量。

intPthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
传递给Pthread_cond_wait 函数的互斥量mutex对条件进行保护,调用者把锁住的互斥量传给函数,函数把调用线程放到等待条件的线程表上,然后对互斥量解锁,这两个操作是原子操纵。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的通道,这样线程就不会错过条件的任何变化,Pthread_cond_wait返回时,互斥量再次被锁住。

有两个函数可以用于通知线程条件已经满足。Pthread_cond_signal 函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有线程。

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

实例:

#include<pthread.h>
struct msg{
    Struct msg *m_next;
    /*more stuff here*/
};
struct 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);
            /*now process the message mp*/
        }
    }
}

void enqueue_msg(struct msg *mp){
    Pthread_mutex_lock(&qlock);
    mp->m_next=workq; //连续接上workq队列
    workq = mp;  //在workq 队列前面添加mp元素
    pthread_mutex_unlock(&qlock);
    pthread_cond_signal(&qready);//发送锁释放信号
}

猜你喜欢

转载自blog.csdn.net/cindywry/article/details/85405564