Меня давно мучает одна интересная особенность при работе с pthread. Когда начинал работать с pthread, нужно было найти утечку памяти в небольшой программке. Использовал mtrace. Что не освобождал - нашел и исправил. Но была обнаружена еще одна утечка - пять плюсов, на которых как раз не хватало несколько минусов (см. mtrace()). Как бы я не пытался исправлять код, избавлялся от динамических массивов - эти пять плюсов все равно присутствовали в логах mtrace.
Когда писал пост про pthread в этот блог, данные плюсы снова у меня всплыли и начали снова мозолить мне глаза. Для начала, давайте посмотрим, что происходит.
Возьмем следующий код, который запускает 5 потоков, которые выводят в консоль простую строчку и завершают свою работу. Под идентификаторы потоков делаем массив threads. Также для примера создадим целочисленный массив arr на 10 элементов, чтобы просто было видно в mtrace как он очищается.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <pthread.h> #include <stdlib.h> #include <mcheck.h> #include <stdio.h> #define N 5 void* threadFunc(void* thread_data){ printf("Hello from thread\n"); pthread_exit(0); } int main(){ mtrace(); int* arr = (int*) malloc(10 * sizeof(int)); pthread_t* threads = (pthread_t*) malloc(N * sizeof(pthread_t)); for(int i = 0; i < N; i++){ pthread_create(&(threads[i]), NULL, threadFunc, NULL); } for(int i = 0; i < N; i++) pthread_join(threads[i], NULL); free(arr); free(threads); return 0; } |
Следует обратить внимание, что потоки завершаются с помощью функции pthread_exit(0). Собственно ниже приведен результат выполнения данной программки и вывод лога mtrace.
Желтым вопросом на картинке помечены те странные строчки. Как видно, память, которая явно выделяется функцией malloc, отлично очищается, как и память, которая выделяется под потоки. Теперь попробуем увеличить количество потоков до 8 (#define N 8) - посмотрим, как поведут себя те загадочные 5 строчек.
Как видно на картинке сверху, количество неочищенной памяти не увеличилось. Можно сделать вывод, что с увеличением количества потоков память не течет (проверял на 100, тот же результат). Возможно, это баг mtrace, а может быть это и не баг вовсе. Также я попробовал проверить программу с 8 потоками на утечку с помощью Valgrind, вот результат:
Valgrind утечку, насколько я понимаю, вообще не обнаружил, что больше наводит на мысль, что это явление не баг.
Методом комментирования всего подряд я нашел очень простой и банальный вариант решения, который избавляет программу от этого проблемного выделения памяти. Я заменил функцию pthread_exit(0) для завершения потока на return 0. Результат приведен ниже (для 5 потоков):
Как видно - ничего лишнего.
Что в итоге это значит, я так и не могу понять, поэтому если знающий человек это читает, буду очень признателен, если этот знающий человек объяснит - почему так и ответит в комментариях.
Однозначного ответа на этот вопрос я так найти и не смог. Но пару интересных ссылок обнаружить удалось, где некоторые господа сообщают о том, что это явление - особенность реализации библиотеки glibc и что это не утечка. Данная память не освобождается сразу по завершению процесса программы, что в результате и было обнаружено. Ниже приведу две ссылки на обсуждения этого вопроса: