Linux线程同步--条件变量

    条件变量是线程间同步的一种方式,线程在条件不满足时阻塞,在条件满足时,由其他线程唤醒,避免了对条件的频繁查询。

    Linux条件变量操作由以下几个函数实现:

int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
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);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

1.      初始化

    条件变量有两种初始化方式:

    静态初始化:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

    动态初始化:

pthread_cond_t *pcond = malloc(sizeof(pthread_cond_t));
pthread_cond_init(pcond, NULL);

2.      等待

    pthread_cond_wait函数和pthread_cond_timedwait函数在条件不满足时等待,通常和一个互斥锁一起使用。例如一个线程要在全局变量x>0时才做某些操作,当x<=0时需要等待:

pthread_mutex_lock(pmutex);
while(x <= 0) {
  pthread_cond_wait(pcond, pmutex);
}
pthread_mutex_unlock(pmutex);

    条件变量本身是多线程共享资源,互斥锁是为了对条件变量进行保护。研究一下pthread_cond_wait函数的内部实现。每个条件变量在内核中对应一个等待队列,pthread_cond_wait函数首先将当前线程放到等待队列上,然后释放互斥锁,这两个操作是原子的,使得while判断到线程添加到等待队列上这段时间内的信号不会丢失(被互斥锁保护)。想象一下,如果先释放互斥锁,然后添加到等待队列上,那么在释放互斥锁后,其他线程可以得到互斥锁,并使条件成立发送唤醒信号,由于当前线程并未添加到等待队列上,导致信号丢失,当然线程将永远阻塞。pthread_cond_wait进入阻塞后,当前线程并不持有互斥锁,其他线程这时可以得到互斥锁并改变条件。pthread_cond_wait收到信号解除阻塞后并不是马上返回,要获取互斥锁然后进入while判断,与while之前的加锁一致。

    为什么要用while判断?而不是用if判断?用if或者while做判断可以保证在调用pthread_cond_wait之前条件已经得到满足(当前线程没有处于等待状态,唤醒信号必然已经丢失了)时,程序能正常运行,不会等待永远也来不了的信号。用while是为了在当前线程被唤醒时,再次判断条件是否真的满足了,pthread_cond_wait返回不一定就是其他线程发送了唤醒信号,也可能是虚假唤醒(spurious wakeups)。

    Linux中慢速系统调用会因为进程收到异步信号而中断,在执行完信号处理函数后返回错误,并且errno被设置为EINTR。用户在设置信号处理函数时可以使用SA_RESTART标识,使系统调用自动重启,也可以手动重启。pthread_cond_wait内部调用了futex系统调用,在futex阻塞时,异步信号使futex返回,但是futex没有重启。如果futex被重启,那么在futex返回之后,重启之前,其他线程的唤醒信号可能丢失,再次阻塞可能就是永远阻塞了,所以这里必须交给用户去重启。从线程角度去看,即pthread_cond_wait返回不一定就是条件真的满足了。

    从软件逻辑上说,pthread_cond_wait返回也要再次验证条件是否满足,如果条件是某种资源,某个线程找到了资源,发出唤醒信号,多个线程都被唤醒,可能当前线程被调度时资源已经被其他线程使用完了。

3.      唤醒

    pthread_cond_signal用于唤醒在条件变量上等待的一个线程,pthread_cond_broadcast用于唤醒在条件变量上等待的所有线程。使用方式如下:
pthread_mutex_lock(pmutex);
x = 1;				// 使条件满足
pthread_cond_signal(pcond);
pthread_mutex_unlock(pmutex);



参考:

http://blog.vladimirprus.com/2005/07/spurious-wakeups.html




你可能感兴趣的:(linux)