Это цифра 11.12 в книге Расширенное программирование в среде UNIX .
Комментарий показывает, что f_count
и f_next
защищены hashlock
.
Однако На рисунке 11.11 f_count
и f_next
защищены разными мьютексами, и код становится слишком сложным для чтения для меня.
Что следует учитывать, когда я использую только один мьютекс вместо двух в этот случай?
Автор дает краткие пояснения (улучшить параллелизм), но я не понимаю, есть ли какие-либо расы или что-то плохое на рисунке 11.12?
#include <pthread.h>
#include <stdlib.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; /* protected by hashlock */
pthread_mutex_t f_lock;
int f_id;
struct foo* f_next; /* protected by hashlock */
/* ... more stuff here ... */
};
struct foo* foo_alloc(int id) /* allocate the object */
{
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);
/* ... continue initialization ... */
pthread_mutex_unlock(&fp->f_lock);
}
return (fp);
}
void foo_hold(struct foo* fp) /* add a reference to the object */
{
pthread_mutex_lock(&hashlock);
fp->f_count++;
pthread_mutex_unlock(&hashlock);
}
struct foo* foo_find(int id) /* find an existing object */
{
struct foo* fp;
pthread_mutex_lock(&hashlock);
for (fp = fh[HASH(id)]; fp != NULL; fp = fp->f_next) {
if (fp->f_id == id) {
fp->f_count++;
break;
}
}
pthread_mutex_unlock(&hashlock);
return (fp);
}
void foo_rele(struct foo* fp) /* release a reference to the object */
{
struct foo* tfp;
int idx;
pthread_mutex_lock(&hashlock);
if (--fp->f_count == 0) { /* last reference, remove from list */
idx = HASH(fp->f_id);
tfp = fh[idx];
if (tfp == fp) {
fh[idx] = fp->f_next;
} else {
while (tfp->f_next != fp) tfp = tfp->f_next;
tfp->f_next = fp->f_next;
}
pthread_mutex_unlock(&hashlock);
pthread_mutex_destroy(&fp->f_lock);
free(fp);
} else {
pthread_mutex_unlock(&hashlock);
}
}
Кроме того, лучше изменить порядок, чтобы избежать тупика в веселье c struct foo* foo_alloc(int id)
.
pthread_mutex_lock(&fp->f_lock);
pthread_mutex_unlock(&hashlock);
/* ... continue initialization ... */
pthread_mutex_unlock(&fp->f_lock);
до
pthread_mutex_unlock(&hashlock);
pthread_mutex_lock(&fp->f_lock);
/* ... continue initialization ... */
pthread_mutex_unlock(&fp->f_lock);