Распределение памяти в многопоточной среде - PullRequest
1 голос
/ 29 апреля 2011

Мне трудно понять, как управлять освобождением памяти в многопоточных средах.В частности, мне трудно использовать блокировку для защиты конструкции, но когда приходит время освободить конструкцию, вы должны разблокировать блокировку, чтобы уничтожить сам замок.Что вызовет проблемы, если отдельный поток ожидает той же блокировки, которую вам нужно уничтожить.

Я пытаюсь найти механизм, который имеет счетчики сохранения, и когда счетчик сохранения объекта равен 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 Ответ

1 голос
/ 29 апреля 2011

Да, вы не можете положить ключ в сейф. Ваш подход с refcount (создать объект по запросу и не существует, удалить в последнем выпуске) является правильным. Но блокировка должна существовать, по крайней мере, за мгновение до того, как объект будет создан и после того, как он будет уничтожен, то есть пока он используется. Вы не можете удалить его изнутри себя.

OTOH, вам не нужно бесчисленное множество блокировок, например, по одному на каждый объект, который вы создаете. Одна блокировка, исключающая получение и освобождение всех объектов, вообще не приведет к значительной потере производительности. Так что просто создайте блокировку при инициализации и уничтожьте при завершении программы. Оформление / освобождение объекта должно быть достаточно коротким, чтобы блокировка для переменной A, блокирующая доступ к несвязанной переменной B, почти никогда не происходила. Если это произойдет - вы все равно можете ввести одну блокировку для всех редко получаемых переменных и по одной для каждой часто получаемой переменной.

Кроме того, кажется, что для rwlock нет смысла, достаточно простого мьютекса, и операции создания / уничтожения ДОЛЖНЫ исключать друг друга, а не только параллельные экземпляры самих себя, поэтому используйте вместо этого семейство pthread_create_mutex ().

...