一、互斥量
互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。
互斥量用pthread_mutex_t数据类型表示,在使用互斥变量以前,必须首先对它进行初始化。可以把它设置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),也可以通过调用pthread_mutex_init函数进行初始化,如果动态地分配互斥量(比如说malloc)那么在释放内存前需要调用pthread_mutex_destroy
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); int pthread_mutex_destroy(pthread_mutex_t *mutex) //返回值:若成功返回0,否则返回错误编号
对互斥量进行加锁,需要调用pthread_mutex_lock,如果互斥量已经上锁,调用线程将阻塞直到互斥量被解锁,对互斥量解锁,需要调用pthread_mutex_unlock
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex) int pthread_mutex_trylock(pthread_mutex_t *mutex) int pthread_mutex_unlock(pthread_mutex_t *mutex) //返回值:若成功返回0,否则返回错误编号
如果线程不希望被阻塞,它可以使用pthread_mutex_trylock尝试对互斥量进行加锁,如果调用pthread_mutex_trylock时互斥量处于未锁住状态,那么pthread_mutex_trylock将锁住互斥量,不会出现阻塞并返回0,否则pthread_mutex_trylock就会失败,不能锁住互斥量,而返回EBUSY。
struct foo { int f_count; pthread_mutex_t f_lock; }; struct foo *foo_alloc(void) { struct foo *fp; if((fp=malloc(sizeof(struct foo)))!=NULL)//动态地分配互斥量 { fp->count=1; if(pthread_mutex_init(&fp->f_lock,NULL)!=0) { free(fp); return(NULL); } } return(fp); } void foo_hold(struct foo *fp) { pthread_mutex_lock(&fp->f_lock); fp->f_count++; pthread_mutex_unlock(&fp->f_lock); } void foo_rele(struct foo *fp) { pthread_mutex_lock(&fp->f_lock); if(--fp->f_count==0) { pthread_mutex_unlock(&fp->f_lock); pthread_mutex_destroy(&fp->f_lock); free(fp); } else pthread_mutex_unlock(&fp->f_lock); }
二、避免死锁
1.控制互斥量加锁顺序:总是让多个互斥量以相同的顺序加锁
2.合理安排锁的功能:如果锁的粒度太粗,就会出现很多线程阻塞等待相同的锁;如果锁的粒度太细,那么过多的锁开销会使系统性能受到影响,而且代码变得复杂。
三、读写锁
读写锁pthread_rwlock_t可以有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。一次只有一个线程可以占有写模式的读写锁pthread_rwlock_wrlock,但是多个线程可以同时占有读模式的读写锁pthread_rwlock_rdlock。
读写锁在使用之前必须初始化,在释放它们底层的内存前必须销毁。
#include <pthread.h> int pthread_rwlock_init(pthread_rwlock_t *restrict rwllock,const pthread_rwlockattr_t *restrict attr);; int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
#include <pthread.h> int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//读模式下锁定读写锁 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//写模式下锁定读写锁 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//解锁
四、条件变量
条件变量pthread_cond_t通常被保存为全局变量,并和互斥锁合作,还需要和另一个全局变量 配合,这个全局变量用来构成各个条件。条件由互斥量保护,线程在改变条件状态前必须首先锁住互斥量。
条件变量在使用之前必须进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,但是如果条件变量是动态分配的,可以使用pthread_cond_init函数初始化。
使用pthread_cond_wait等待条件变为真,如果在给定时间内条件不能满足,会返回一个代表出错码的返回变量
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
pthread_cond_signal函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有线程。
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
struct msg { struct msg *m_next; }; 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)//当workq为NULL时,等待cond通知 pthread_cond_wait(&qready,&qlock); mp=workq; workq=mp->m_next; pthread_mutex_unlock(&qlock); } } void enqueue_msg(struct msg *mp) { pthread_mutex_lock(&qlock); mp->m_next=workq; workq=mp; pthread_mutex_unlock(&qlock); pthread_cond_signal(&qready); }
个人感觉条件变量的作用在于发送消息通知程序条件已经满足,继续下面的程序,互斥量保护全局变量构成的条件,当条件不满足时,使用条件变量对应的函数来阻塞条件。