多线程操作哈希表避免死锁

copy自《UNIX环境高级编程》图11.11。

#include 
#include 

#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;
    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(&fp->f_lock);
    fp->f_count++;
    pthread_mutex_unlock(&fp->f_lock);
}

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) {
            foo_hold(fp);
            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(&fp->f_lock);
    if (fp->f_count == 1) { /* last reference */
        pthread_mutex_unlock(&fp->f_lock);
        pthread_mutex_lock(&hashlock);
        pthread_mutex_lock(&fp->f_lock);
        /* need to recheck the condition */
        if (fp->f_count != 1) {
            fp->f_count--;
            pthread_mutex_unlock(&fp->f_lock);
            pthread_mutex_unlock(&hashlock);
            return;
        }
        /* 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_unlock(&fp->f_lock);
        pthread_mutex_destroy(&fp->f_lock);
        free(fp);
    } else {
        fp->f_count--;
        pthread_mutex_unlock(&fp->f_lock);
    }
}

程序基本流程:

fh    哈希表(链表解决散列冲突)

foo    哈希表节点(指向对象)

foo_alloc    申请节点
    1、申请节点内存
    2、插入到哈希表
        2.1、哈希表加锁(hashlock)
        2.2、插入新节点
        2.3、节点加锁(此时节点已经添加到哈希表中了)
        2.4、哈希表解锁
        2.5、其他初始化动作
        2.6、节点解锁

foo_hold    拿住节点
    1、节点加锁(所有节点都应该是通过foo_alloc获取的,所以使用的时候已经在哈希表中了)
    2、节点引用计数加1
    2、节点解锁

foo_find    查找节点
    1、哈希表加锁(避免此时有申请或者释放节点)
    2、查找节点
    3、哈希表解锁

foo_rele    释放节点
    1、节点加锁
    2、判断节点引用计数是否为1
        2.1、节点引用计数为1,做以下动作
            a. 节点解锁,哈希表加锁
            b. 节点加锁
                判断此时节点引用计数是否为1,不为1则将引用计数减1
                然后解锁节点、解锁哈希表,函数返回
            c. 从哈希表中移除节点
            d. 哈希表解锁
            e. 节点解锁
            f. 释放节点内存(需要释放锁)
        2.2、节点引用计数不为1
            节点引用计数减1
            节点解锁

foo_rele:

多线程操作哈希表避免死锁_第1张图片

参考:

https://segmentfault.com/q/1010000007227951

你可能感兴趣的:(Linux与网络编程)