互斥量mutex
#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);
int pthread_mutex_lock(pthread_mutex_t *mutex);
//对互斥量加锁,如果已经加锁,线程阻塞直至被解锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//如果已经加锁,不会阻塞,而是返回EBUSY
int pthread_mutex_unlock(pthread_mutex_t *mutex);
下面的例子演示对一个对象进行引用计数。引用计数是受互斥量保护的,每回hold对象时,加锁,引用计数加一;release时,解锁,引用计数减一。例外情况是在初始化时,对象刚被创造,不需要加锁。以及对象最后一个引用释放时同时释放空间。
// APUE 11-10
// use mutex to protect data
// lcl 20190324
//
#include "../myapue.h"
#include <pthread.h>
struct foo {
int f_count;
pthread_mutex_t f_lock;
int f_id;
/* more stuff here */
};
struct foo* foo_alloc(int id)
{
struct foo * fp;
if ((fp = malloc(sizeof(struct foo))) != NULL) {
fp->f_count = 1;
fp->f_id = id;
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_destory(&fp->f_lock);
free(fp);
}
else
{
pthread_mutex_unlock(&fp->f_lock);
}
}
避免死锁,使用两个互斥锁时,需要操作时指定加锁顺序,否则可能造成两个资源相互持有对方需要的锁,一直处于阻塞状态。下例中,既有保护对象的锁f_lock,也有对象所在的全局哈希表的锁hashlock,当同时使用两个锁时,每次操作都按照先锁hashlock再锁f_lock。
// APUE 11-11
// use two mutex to protect struct and hash
// lcl 20190324
//
#include "../myapue.h"
#include <pthread.h>
#define NHASH 29
#define HASH(id) (((unsigned long)id)%NHASH)
struct foo *fh[NHASH];
pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;
struct foo {
int f_count;
pthread_mutex_t f_lock;
int f_id;
/* more stuff here */
struct foo *f_next;
};
struct foo* foo_alloc(int id)
{
struct foo * fp;
int idx;
if ((fp = malloc(sizeof(struct foo))) != NULL) {
fp->f_count = 1;
fp->f_id = id;
if (pthread_mutex_init(&fp->f_lock, NULL) != 0) {
free(fp);
return(NULL);
}
idx = HASH(id);
pthread_mutex_lock(&hashlock);
fp->f_next = fh[idx];
fh[idx] = fp;
pthread_mutex_lock(&fp->f_lock);
pthread_mutex_unlock(&hashlock);
/* ... */
pthread_mutex_unlock(&fp->f_lock);
}
return(fp);
}
void foo_hold(struct foo *fp)
{
pthread_mutex_lock(&fp->f_lock);
fp->f_count++;
pthread_mutex_unlock(&fp->f_lock);
}
struct foo_find(int id)
{
struct foo *fp;
pthread_mutex_lock(&hashlock);
for (fp = fh[HASH(id)]; fp != NULL; fp = fp->f_next){
if (fp->f_id == id){
foo_hold(fp);
break;
}
}
pthread_mutex_unlock(&hashlock);
return(fp);
}
void foo_rele(struct foo * fp)
{
struct foo *fp;
int idx;
pthread_mutex_lock(&fp->f_lock);
if (fp->f_count == 1) {
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_lock(&hashlock);
pthread_mutex_lock(&fp->f_lock);
if (fp->f_count != 1)
{
fp->f_count--;
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_unlock(&hashlock);
return;
}
idx = HASH(fp->f_id);
tfp = fh[idx];
if(tfp == fp){
fh[idx] = fp ->f_next;
}
else
{
while(tfp->next != fp)
{
tfp = tfp->next;
}
tfp->next = fp->next;
}
pthread_mutex_unlock(&hashlock);
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_destory(&fp->f_lock);
free(fp);
}
else
{
fp->f_count--;
pthread_mutex_unlock(fp->f_lock);
}
}
这样做的负担显而易见,尤其是release时,因为最后一个引用release时,需要删除,又需要操作哈希表,按照加锁顺序的要求,此时要先释放f_lock,再锁hashlock,再锁f_lock,而再这个过程中,其他线程可能又占用了该资源,需要重新检查计数。。
若只使用hashlock来管理,则会变得简单。
void foo_hold(struct foo *fp)
{
pthread_mutex_lock(&hashlock);
fp->f_count++;
pthread_mutex_unlock(&hashlock);
}
void foo_rele(struct foo * fp)
{
pthread_mutex_lock(&hashlock);
if (--fp->f_count == 0) {
pthread_mutex_unlock(&hashlock);
pthread_mutex_destory(&hashlock);
free(fp);
}
else
{
pthread_mutex_unlock(&hashlock);
}
}
但是这样的锁太粗,容易造成多个线程等待同一把锁,影响性能。所以需要进行性能和复杂的平衡。
网友评论