创建复杂的同步对象
线程库中提供的子例程能够用作原语来构建更复杂的同步对象。
长锁
线程库提供的互斥对象是低争用对象,并且不应该被保留很长时间。 长锁是通过互斥对象和条件变量实现的,所以长锁能被保留很长时间而不影响程序性能。 如果启用了可取消,就不应该使用长锁。
长锁的数据类型为 long_lock_t。 它必须通过 long_lock_init 例程初始化。 long_lock, long_trylock和 long_unlock 子例程执行与 pthread_mutex_lock类似的操作。 pthread_mutex_trylock和 pthread_mutex_unlock 子例程。
typedef struct {
pthread_mutex_t lock;
pthread_cond_t cond;
int free;
int wanted;
} long_lock_t;
void long_lock_init(long_lock_t *ll)
{
pthread_mutex_init(&ll->lock, NULL);
pthread_cond_init(&ll->cond);
ll->free = 1;
ll->wanted = 0;
}
void long_lock_destroy(long_lock_t *ll)
{
pthread_mutex_destroy(&ll->lock);
pthread_cond_destroy(&ll->cond);
}
void long_lock(long_lock_t *ll)
{
pthread_mutex_lock(&ll->lock);
ll->wanted++;
while(!ll->free)
pthread_cond_wait(&ll->cond);
ll->wanted--;
ll->free = 0;
pthread_mutex_unlock(&ll->lock);
}
int long_trylock(long_lock_t *ll)
{
int got_the_lock;
pthread_mutex_lock(&ll->lock);
got_the_lock = ll->free;
if (got_the_lock)
ll->free = 0;
pthread_mutex_unlock(&ll->lock);
return got_the_lock;
}
void long_unlock(long_lock_t *ll)
{
pthread_mutex_lock(&ll->lock);
ll->free = 1;
if (ll->wanted)
pthread_cond_signal(&ll->cond);
pthread_mutex_unlock(&ll->lock);
}
信号量
UNIX系统中的传统 semaphores 是进程间同步设施。 对于特定用法,您能够实现线程间信号。
信号量具有 sema_t 数据类型。 它必须由 sema_init 例程初始化,并使用 sema_destroy 例程销毁。 信号 wait 和信号 post 操作分别由 sema_p 和 sema_v 例程执行。
typedef struct {
pthread_mutex_t lock;
pthread_cond_t cond;
int count;
} sema_t;
void sema_init(sema_t *sem)
{
pthread_mutex_init(&sem->lock, NULL);
pthread_cond_init(&sem->cond, NULL);
sem->count = 1;
}
void sema_destroy(sema_t *sem)
{
pthread_mutex_destroy(&sem->lock);
pthread_cond_destroy(&sem->cond);
}
void p_operation_cleanup(void *arg)
{
sema_t *sem;
sem = (sema_t *)arg;
pthread_mutex_unlock(&sem->lock);
}
void sema_p(sema_t *sem)
{
pthread_mutex_lock(&sem->lock);
pthread_cleanup_push(p_operation_cleanup, sem);
while (sem->count <= 0)
pthread_cond_wait(&sem->cond, &sem->lock);
sem->count--;
/*
* Note that the pthread_cleanup_pop subroutine will
* execute the p_operation_cleanup routine
*/
pthread_cleanup_pop(1);
}
void sema_v(sema_t *sem)
{
pthread_mutex_lock(&sem->lock);
if (sem->count <=0)
pthread_cond_signal(&sem->cond);
sem->count++;
pthread_mutex_unlock(&sem->lock);
}
计数器指定允许使用信号的用户数。 它并不一定必须为负值;因此它不指定等待用户数(如传统信号)。 此实现为 pthread_cond_wait 子例程上的多个唤醒问题提供典型的解决方案。 信号等待操作是可取消的,因为 pthread_cond_wait 子例程提供了一个取消点。
写优先的读/写锁
写优先的读/写锁为多个线程提供对受保护资源的同时只读访问,并为单个线程提供对资源的写访问,同时排除读访问。 当写操作释放锁时,其他等待的写程序将在任何等待的读程序之前获取锁。 写优先的读/写锁通常用来保护读比写频繁得多的资源。
写优先的读/写锁的数据类型为 rwlock_t。 它必须通过 rwlock_init 例程初始化。 rwlock_lock_read 例程锁定读程序的锁(允许多个读程序),rwlock_unlock_read 例程为其解锁。 rwlock_lock_write 例程锁定写程序的锁,rwlock_unlock_write 例程为其解锁。 必须调用合适的解锁例程(用于读程序或者写程序的解锁例程)。
typedef struct {
pthread_mutex_t lock;
pthread_cond_t rcond;
pthread_cond_t wcond;
int lock_count; /* < 0 .. held by writer */
/* > 0 .. held by lock_count readers */
/* = 0 .. held by nobody */
int waiting_writers; /* count of wating writers */
} rwlock_t;
void rwlock_init(rwlock_t *rwl)
{
pthread_mutex_init(&rwl->lock, NULL);
pthread_cond_init(&rwl->wcond, NULL);
pthread_cond_init(&rwl->rcond, NULL);
rwl->lock_count = 0;
rwl->waiting_writers = 0;
}
void waiting_reader_cleanup(void *arg)
{
rwlock_t *rwl;
rwl = (rwlock_t *)arg;
pthread_mutex_unlock(&rwl->lock);
}
void rwlock_lock_read(rwlock_t *rwl)
{
pthread_mutex_lock(&rwl->lock);
pthread_cleanup_push(waiting_reader_cleanup, rwl);
while ((rwl->lock_count < 0) && (rwl->waiting_writers))
pthread_cond_wait(&rwl->rcond, &rwl->lock);
rwl->lock_count++;
/*
* Note that the pthread_cleanup_pop subroutine will
* execute the waiting_reader_cleanup routine
*/
pthread_cleanup_pop(1);
}
void rwlock_unlock_read(rwlock_t *rwl)
{
pthread_mutex_lock(&rwl->lock);
rwl->lock_count--;
if (!rwl->lock_count)
pthread_cond_signal(&rwl->wcond);
pthread_mutex_unlock(&rwl->lock);
}
void waiting_writer_cleanup(void *arg)
{
rwlock_t *rwl;
rwl = (rwlock_t *)arg;
rwl->waiting_writers--;
if ((!rwl->waiting_writers) && (rwl->lock_count >= 0))
/*
* This only happens if we have been canceled
*/
pthread_cond_broadcast(&rwl->wcond);
pthread_mutex_unlock(&rwl->lock);
}
void rwlock_lock_write(rwlock_t *rwl)
{
pthread_mutex_lock(&rwl->lock);
rwl->waiting_writers++;
pthread_cleanup_push(waiting_writer_cleanup, rwl);
while (rwl->lock_count)
pthread_cond_wait(&rwl->wcond, &rwl->lock);
rwl->lock_count = -1;
/*
* Note that the pthread_cleanup_pop subroutine will
* execute the waiting_writer_cleanup routine
*/
pthread_cleanup_pop(1);
}
void rwlock_unlock_write(rwlock_t *rwl)
{
pthread_mutex_lock(&rwl->lock);
l->lock_count = 0;
if (!rwl->wating_writers)
pthread_cond_broadcast(&rwl->rcond);
else
pthread_cond_signal(&rwl->wcond);
pthread_mutex_unlock(&rwl->lock);
}
仅对阅读器计数。 当计数到达零时,正在等待的写程序可能获得锁。 只有一个写程序能够持有锁。 当锁被写程序释放后,如果有另一个写程序,那么将其唤醒。 否则,所有正在等待的阅读器被唤醒。
锁定例程是可取消的,因为它们调用 pthread_cond_wait 子例程。 因此,清除处理程序在调用子例程之前被注册。