在多线程环境中释放内存

时间:2011-04-29 07:57:55

标签: c locking pthreads

我很难弄清楚如何在多线程环境中管理内存释放。具体来说,我正在使用锁来保护结构,但是当它需要释放结构时,你必须解锁锁以破坏锁本身。如果一个单独的线程正在等待你需要销毁的同一个锁,那么这将导致问题。

我正在尝试提出一种保留计数的机制,当对象的保留计数为0时,它全部被释放。我一直在尝试一些不同的东西,但只是无法做到正确。正如我一直在做的那样,你似乎无法将锁定机制置于你需要能够释放和销毁的结构内部,因为这需要你解锁其中的锁,这可能允许另一个线程如果它在同一结构的锁定请求中被阻止则继续。这意味着肯定会发生一些未定义的事情 - 锁被破坏,并且被解除分配,因此要么你得到内存访问错误,要么锁定未定义的行为..

有人会介意看我的代码吗?我能够整理一个沙盒示例,演示我正在尝试没有一堆文件。

http://pastebin.com/SJC86GDp

#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

struct xatom {
    short rc;
    pthread_rwlock_t * rwlck;
};
typedef struct xatom xatom;

struct container {
    xatom * atom;
};
typedef struct container container;

#define nr 1
#define nw 2
pthread_t readers[nr];
pthread_t writers[nw];

container * c;

void  retain(container * cont);
void  release(container ** cont);
short retain_count(container * cont);

void * rth(void * arg) {
    short rc;
    while(1) {
        if(c == NULL) break;
        rc = retain_count(c);
    }
    printf("rth exit!\n");
    return NULL;
}

void * wth(void * arg) {
    while(1) {
        if(c == NULL) break;
        release((container **)&c);
    }
    printf("wth exit!\n");
    return NULL;
}

short retain_count(container * cont) {
    short rc = 1;
    pthread_rwlock_rdlock(cont->atom->rwlck);
    printf("got rdlock in retain_count\n");
    rc = cont->atom->rc;
    pthread_rwlock_unlock(cont->atom->rwlck);
    return rc;
}

void retain(container * cont) {
    pthread_rwlock_wrlock(cont->atom->rwlck);
    printf("got retain write lock\n");
    cont->atom->rc++;
    pthread_rwlock_unlock(cont->atom->rwlck);
}

void release(container ** cont) {
    if(!cont || !(*cont)) return;
    container * tmp = *cont;
    pthread_rwlock_t ** lock = (pthread_rwlock_t **)&(*cont)->atom->rwlck;
    pthread_rwlock_wrlock(*lock);
    printf("got release write lock\n");
    if(!tmp) {
        printf("return 2\n");
        pthread_rwlock_unlock(*lock);
        if(*lock) {
            printf("destroying lock 1\n");
            pthread_rwlock_destroy(*lock);
            *lock = NULL;
        }
        return;
    }
    tmp->atom->rc--;
    if(tmp->atom->rc == 0) {
        printf("deallocating!\n");
        *cont = NULL;
        pthread_rwlock_unlock(*lock);
        if(pthread_rwlock_trywrlock(*lock) == 0) {
            printf("destroying lock 2\n");
            pthread_rwlock_destroy(*lock);
            *lock = NULL;
        }
        free(tmp->atom->rwlck);
        free(tmp->atom);
        free(tmp);
    } else {
        pthread_rwlock_unlock(*lock);
    }
}

container * new_container() {
    container * cont = malloc(sizeof(container));
    cont->atom = malloc(sizeof(xatom));
    cont->atom->rwlck = malloc(sizeof(pthread_rwlock_t));
    pthread_rwlock_init(cont->atom->rwlck,NULL);
    cont->atom->rc = 1;
    return cont;
}

int main(int argc, char ** argv) {
    c = new_container();
    int i = 0;
    int l = 4;
    for(i=0;i<l;i++) retain(c);
    for(i=0;i<nr;i++) pthread_create(&readers[i],NULL,&rth,NULL);
    for(i=0;i<nw;i++) pthread_create(&writers[i],NULL,&wth,NULL);
    sleep(2);
    for(i=0;i<nr;i++) pthread_join(readers[i],NULL);
    for(i=0;i<nw;i++) pthread_join(writers[i],NULL);
    return 0;
}

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

是的,你不能把钥匙放在保险箱内。您的refcount方法(在请求时创建对象但不存在,在上次发布时删除)是正确的。但是锁必须至少在创建对象之前和销毁之后存在 - 也就是说,在使用它时。你无法从内部删除它。

OTOH,你不需要无数锁,就像你创建的每个对象一样。排除所有对象的获取和释放的一个锁将不会产生很多性能损失。所以只需在init上创建锁定并在程序结束时销毁。获取/释放对象应该足够短,以至于锁定变量A几乎不会发生对无关变量B的访问。如果它发生了 - 你仍然可以为每个很少获得的变量引入一个锁,并且每个经常获得一个变量。

此外,rwlock似乎没有意义,普通的互斥体就足够了,并且创建/销毁操作必须相互排斥,而不仅仅是它们自己的并行实例 - 所以请改用pthread_create_mutex()系列。