条件变量、自旋锁和屏障

    条件变量本身是由互斥量保护的,线程在改变条件状态之前必须首先锁住互斥量。
    下面一组函数可用来操作条件变量。
#include <pthread.h>
int pthread_cond_init( pthread_cond_t *restrict cond,
                       const 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 *restirct tsptr);

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
                       /* 所有函数的返回值:若成功,返回 0;否则,返回错误编号 */

    pthread_cond_init 和 pthread_cond_destroy 函数分别用来初始化和销毁条件变量。参数 attr 可用来设置条件变量的属性,为 NULL 时表示使用默认属性。对于静态分配的条件变量,可以使用 PTHREAD_COND_INITIALIZER 常量来进行初始化。
    pthread_cond_wait 会等待条件变量变为真。传递给 pthread_cond_wait 的互斥量可对条件进行保护。调用者把锁住的互斥量传给函数,然后函数会自动把调用线程放到等待条件的线程列表上,并对互斥量解锁。这就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。该函数返回时,互斥量会再次被锁住。pthread_cond_timedwait 函数同 pthread_cond_wait,不过提供了定时功能。如果到达指定的时刻条件还没出现,该函数将重新获取互斥量,然后返回错误 ETIMEDOUT。注意这两个函数在成功返回时,线程需要重新计算条件,因为另一个线程可能在运行并改变了条件。
    pthread_cond_signal 和 pthread_cond_broadcast 可用来通知线程条件已经满足,前者至少能唤醒一个等待该条件的线程,而后者则能唤醒所有等待该条件的线程。
    下面这个程序片段演示了如何结合条件变量和互斥量来同步线程。
#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	lock = PTHREAD_MUTEX_INITIALIZER;

void process_msq(void){
	for(;;){
		pthread_mutex_lock(&lock);
		while(workq == NULL)
			pthread_cond_wait(&qready, &lock);
		struct msg *mp = workq;
		workq = workq->m_next;
		pthread_mutex_unlock(&lock);
		/* now process mp */
	}
}

void enqueue_msg(struct msg *mp){
	pthread_mutex_lock(&lock);
	mp->m_next = workq;
	workq = mp;
	pthread_mutex_unlock(&lock);
	pthread_cond_signal(&qready);
}


    自旋锁与互斥量类似,但它不是通过休眠使进程阻塞,而是在获取锁前一直处于忙等待状态。因此它一般用于这种情况:锁被持有的时间短,而且线程并不希望在重新调度上花费太多的成本。
    自旋锁也有一组类似于互斥量的函数。
#include <pthread.h>
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
int pthread_spin_destroy(pthread_spinlock_t *lock);

int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);
                      /* 所有函数的返回值:若成功,返回 0;否则,返回错误编号 */

    pthread_spin_init 对自旋锁进行初始化,pthread_spin_destroy 则对其进行反初始化。只有一个属性是自旋锁持有的,该属性只在支持线程进程共享同步选项的平台上才用得到。pshared 参数表示进程共享属性,表明自旋锁是如何获取的。如果它为 PTHREAD_PROCESS_SHARED,则自旋锁能被可以访问锁底层内存的线程所获取,即使它们属于不同的进程;而如果它为 PTHREAD_PROCESS_PRIVATE,则自旋锁就只能被初始化该锁的进程内部的线程所访问。
    pthread_spin_lock 和 pthread_spin_trylock 可对自旋锁加锁。前者在获取锁前一直阻塞,后者如果不能获取锁就立即返回 EBUSY 错误而不会自旋。两种方式加的锁都可以使用 pthread_spin_unlock 进行解锁。

    屏障是用户协调多个用户并行工作的同步机制,它允许每个线程等待,直到所有的合作线程都达到某一点后再继续运行。pthread_join 函数就是一种屏障的例子。
    屏障也有一组操作函数。
#include <pthread.h>
int pthread_barrier_init(pthread_barrier_t *restrict barrier,
                 const pthread_barrierattr_t *restrict attr, unsigend int count);
int pthread_barrier_destroy(pthread_barrier_t *barrier);
                      /* 两个函数的返回值:若成功,返回 0;否则,返回错误编号 */
int pthread_barrier_wait(pthread_barrier_t *barrier);
    /* 返回值:若成功,返回 0 或者 PTHREAD_BARRIER_SERIAL_THREAD;否则,返回错误编号*/

    使用 pthread_barrier_init 初始化屏障时,可以用 count 参数指定在允许所有线程继续运行之前,必须到达屏障的线程数目。attr 参数是屏障属性,为 NULL 时表示使用默认属性。pthread_barrier_destroy 函数则用来释放分配给屏障的资源。
    pthread_barrier_wait 函数可用来等待其他线程赶上来。调用该函数的线程在屏障计数未满足条件时会进入休眠状态。如果该线程是最后一个调用该函数的线程,就满足了屏障计数,所有的线程都被唤醒。对于任意一个线程,该函数返回了 PTHREAD_BARRIER_SERIAL_THREAD,其他线程看到的返回值是 0。这使得一个线程可以作为主线程工作在其他所有线程已完成的工作结果上。一旦达到屏障计数,而且线程处于非阻塞状态,屏障就可以被重用。但除非在调用了 pthread_barrier_destroy 后又调用 pthread_barrier_init 修改了计数,否则屏障计数不会改变。

猜你喜欢

转载自aisxyz.iteye.com/blog/2398843