Есть приложение, написанное на Си, использующее нити, работает по следующей схеме:При запуске приложения запускается основной поток далее по возникновению какого то события из этого потока запускаются другие потоки использующие одну глобальную переменную, память под которую выделяется malloc'ом в основном потоке и освобождается free в потоках которые запускаются из основного. Доступ к этой переменной разделяется с помощью мьютексов. Вроде прога работает, все нормально, но вот только память используемая ей все время растет (top'ом смотрю).
Подскажите в чем может быть причина?
Спасибо.
Да, забыл, все это дело крутится на FreeBSD 4.6.2
Покажите код, хотя бы те участки (только не одну строку), где выделяется и освобождается память. Иначе вам вряд ли кто-нить сможет помочь :)А так на вскидку скорее всего происходит так, что главный поток заново выделяет память до того, как её освободил один из дочерних, указатель теряется - память тоже.
Код примерно такой.
*****************************************
//Основной потокtypedef struct
{
int Поле;
}pkt;.........
//packet - Глобальная переменная
packet = (pkt *)calloc(Размер массива,sizeof(pkt));
i = 0;
while(1) {....
Заполняем packet (packet[i].Поле = i)
....
if (i == Размер массива)
{
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
//db_storage_thread - Глобальная переменная
pthread_create (&db_storage_thread, &attr,&my_proccess, (void *)packet);pthread_detach(db_storage_thread);
packet = (pkt *)calloc(Размер массива,sizeof(pkt));
i = 0;
}else{
i++;
}} /* End while */
******************************************
//my_proccessvoid my_proccess(pkt *packet)
{for (i=0;i < Размер массива; i++)
{
......Что то делаем с packet (сладываем каждый packet[i].поле в файл)
......
}free(packet);
}******************************
Происходит наверное именно так т.е. дочерний поток не успевает освободить память до того как основной ее заново выделит. Как такого избежать? Использовать realloc?
Забыл, pkt - глобальная
realloc тут не поможет.самое лучшее решение - изменить дизайн программы, как уже советовал NL.
Другое трудно предложить - трудно представить что может происходить, когда несколько потоков работают с одним куском памяти, один из них освобождает эту память, а другие при этом ничего не ведают. Очень странно, что программа вообще работает, не вылетая по segmentation fault.
Было что-то такое...>void my_proccess(pkt *packet) {
- вот здесь нужно было бы:
1. заблокироваться на каком-то механизме синхронизации, напр. на критическрй секции (с вызывающей программой);
2. сделать копию *packet - не указателя, а всей структуры...
3. снять блокировку, и уже можно free ... уже не нужно.
4. для всего этого лучше при запуске определить в attr повышенный приоритет потока...>
> for (i=0;i < Размер массива; i++)
> {
>
> ......
>
> Что то делаем с packet (сладываем
>каждый packet[i].поле в файл)
>
> ......
>
> }
>
>free(packet);- а вот здесь хорошо бы посмотреть (проанализировать) код завершения free, она же не void ... вообще проверка всех кодов завершения, до уровня параноидальности ;-) - хорошая привычка.
Хотя реализация эта вся - безусловно плохая.
Почему вызывающая программа не могла бы создавать копию *packet для каждого порождаемого потока, например?
В том то и дело что блокироваться с вызывающем программой нельзя. Основной цикл (в котором потоки порождаются) должен продолжаться без остановки.>Почему вызывающая программа не могла бы создавать копию *packet для каждого порождаемого потока, например?
А как это реализовать (без остановки основного цикла)в данном случае я что то не пойму.
>В том то и дело что блокироваться с вызывающем программой нельзя. Основной
>цикл (в котором потоки порождаются) должен продолжаться без остановки.Давайте смотреть на вещи реально: такого не бывает - хотя бы на порождение thread ваша главная ветка блокируется, а порождение thread (по накладным расходам) - это не вызов функции с передачей параметров в регистрах...
Вопрос всегда в том - на сколько блокироваться? на 1мксек, 1мсек, 1сек... А дополнительное (ко времени создания thread-а) время копирования блока параметров ... вряд ли особо существенно.
>>Почему вызывающая программа не могла бы создавать копию *packet для каждого порождаемого потока, например?
>А как это реализовать (без остановки основного цикла)в данном случае я что то не пойму.Как-то так - в вызывающей программе:
while(1) {
.... //вот здесь каждый раз выделяется блок параметров
pkt* packet = (pkt*)calloc(Размер массива,sizeof(pkt));
// никаких глобальных переменных... и заполняется...
pthread_create (&db_storage_thread, &attr,&my_proccess, (void *)packet);
...а в функции потока:
void my_proccess( pkt* packet ) {
......
Что то делаем с packet
free( packet );
}но ещё лучше - я бы сразу делал дубликат блока параметров - уничтожал переданный блок, а потом что-то делал с дубликатом:
void my_proccess( pkt* packet ) {
pkt dubl( *packet );
free( packet );
......
Что то делаем с dubl
}Обр. вним. - это решение тоже упрощённое, и имеет дефекты синхронизации (об этом и обсуждалось в тех URL, которые я писал) - но оно уже гораздо лучше.
>но ещё лучше - я бы сразу делал дубликат блока параметров - уничтожал переданный блок, а потом что-то делал с дубликатом:
>void my_proccess( pkt* packet ) {
> pkt dubl( *packet );
> free( packet );
> ......
> Что то делаем с dubl
>}Спасибо! Вроде помогло. Но только один момент:
не
pkt dubl(*packet);а
pkt *dubl=packet;
Еще раз спасибо!
Вопрос в догонку. Если я вместо такой реализации массива сделаю линейный список, плюс в скорости получу?
>Вопрос в догонку. Если я вместо такой реализации массива сделаю линейный список,
>плюс в скорости получу?Нет, получите "минус", достаточно значительный ... я думаю.
Смотрите STL - vector - вот с ним можете получить и динамичность, и скорость...
Но это - C++!.
Хотя gcc - до фени - вопрос вкуса юзера ;-)
>>но ещё лучше - я бы сразу делал дубликат блока параметров - уничтожал переданный блок, а потом что-то делал с дубликатом:
>>void my_proccess( pkt* packet ) {
>> pkt dubl( *packet );
>> free( packet );
>> ......
>> Что то делаем с dubl
>>}
>
>Спасибо! Вроде помогло. Но только один момент:
>
>не
>
> pkt dubl(*packet);
>
>а
>
> pkt *dubl=packet;
>
>Еще раз спасибо!
>Если у вас есть конструктор по-умолчанию для структуры pkt - а он всегда есть ... если вы не испортили сами, руками - то и 1-е - сработает.
Правда ... подумалось ... это всё в терминологии C++, я в ней имел в виду.
1) лучше писать прогу так: трэд, который выделил себе блок памяти, его же и освобожддает, а не поручать free другим трэдам. Так проще отследить, что откуда берется да и код становится понятнее и путаницы меньше.
2) используй realloc в основном трэде и тогда в остальных трэдах free можно выкинуть
>1) лучше писать прогу так: трэд, который выделил себе блок памяти, его
>же и освобожддает, а не поручать free другим трэдам. Так проще
> отследить, что откуда берется да и код становится понятнее и
>путаницы меньше.С блоком параметров запуска thread (то, что показано в мсходном коде) - это обычно не проходит, особенно, если thread не join, да и там ... тягомутина. Так что эта задача: синхронизация доступа и разрушения блока параметров - совсем не такая частная, и возникает...
Посмотрите здесь:
http://qnx.org.ru/forum/viewtopic.php?topic=266&forum=4&star...
http://qnx.org.ru/forum/viewtopic.php?topic=1376&forum=4
- может на что натолкнёт...
> При запуске приложения запускается основной поток далее по возникновению какого
>то события из этого потока запускаются другие потоки использующие одну глобальную
>переменную, память под которую выделяется malloc'ом в основном потоке и освобождается
>free в потоках которые запускаются из основного. Доступ к этой переменной
>разделяется с помощью мьютексов. Вроде прога работает, все нормально, но вот
>только память используемая ей все время растет (top'ом смотрю).Вот, нашёл случайно - детальное обсуждение синхронизации ровно для того частого случая, который обсуждается: выделение блока параметров и передача его detached thread, который его и освобождает:
http://qnx.org.ru/forum/viewtopic.php?topic=928&forum=4