pthread_exit против возврата - PullRequest
37 голосов
/ 02 октября 2010

У меня есть присоединяемая функция pthread runner, определенная ниже:

void *sumOfProducts(void *param)
{
...
pthread_exit(0);
}

Предполагается, что этот поток присоединится к основному.

Всякий раз, когда я запускал свою программу через Valgrind, я получал следующие утечки :

LEAK SUMMARY:
   definitely lost: 0 bytes in 0 blocks
   indirectly lost: 0 bytes in 0 blocks
     possibly lost: 0 bytes in 0 blocks
   still reachable: 968 bytes in 5 blocks
        suppressed: 0 bytes in 0 blocks

ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 10)

Я проверил страницу справочника на pthreads, в которой было написано:

  The new thread terminates in one of the following ways:

   * It  calls  pthread_exit(3),  specifying  an exit status value that is
     available  to  another  thread  in  the  same  process   that   calls
     pthread_join(3).

   * It  returns  from  start_routine().   This  is  equivalent to calling
     pthread_exit(3) with the value supplied in the return statement.

   * It is canceled (see pthread_cancel(3)).

   * Any of the threads in the process calls exit(3), or the  main  thread
     performs  a  return  from main().  This causes the termination of all
     threads in the process.

Чудесным образом, когда я заменил pthread_exit () на оператор return, утечки исчезли .

return(NULL);

Мой актуальный вопрос состоит из трех частей:

  1. Может кто-нибудь объяснить, почему в операторе возврата нет утечек?
  2. Есть ли какая-то принципиальная разница между обоими утверждениями в отношении выхода из потоков?
  3. Если так, то когда один должен быть предпочтительнее другого?

Ответы [ 5 ]

40 голосов
/ 02 октября 2010

Следующий минимальный тестовый пример демонстрирует поведение, которое вы описываете:

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

void *app1(void *x)
{
    sleep(1);
    pthread_exit(0);
}

int main()
{
    pthread_t t1;

    pthread_create(&t1, NULL, app1, NULL);
    pthread_join(t1, NULL);

    return 0;
}

valgrind --leak-check=full --show-reachable=yes показывает 5 блоков, выделенных из функций, вызываемых pthread_exit(), которые являются несвободными, но все же достижимыми при выходе из процесса. Если pthread_exit(0); заменено на return 0;, 5 блоков не выделяются.

Однако, если вы протестируете создание и объединение большого количества потоков, вы обнаружите, что количество неосвобожденной памяти, используемой при выходе, увеличивается , а не . Это и тот факт, что он все еще доступен, указывает на то, что вы просто видите странность реализации glibc. Несколько функций glibc выделяют память с malloc() при первом вызове, который они сохраняют для оставшейся части времени жизни процесса. glibc не заботится о том, чтобы освободить эту память при выходе из процесса, поскольку знает, что процесс все равно прерывается - это просто пустая трата циклов процессора.

11 голосов
/ 09 мая 2011

Не уверен, если вы все еще заинтересованы в этом, но я сейчас отлаживаю похожую ситуацию.Потоки, использующие pthread_exit, заставляют valgrind сообщать о достижимых блоках.Причина, по-видимому, довольно хорошо объяснена здесь:

https://bugzilla.redhat.com/show_bug.cgi?id=483821

По существу, кажется, pthread_exit вызывает dlopen, который никогда не очищается явно при выходе из процесса.

1 голос
/ 03 июня 2013

Похоже, что вызов exit () (и, очевидно, pthread_exit ()) оставляет автоматически распределенные переменные выделенными. Вы должны либо вернуться, либо бросить, чтобы правильно раскрутиться.

За C ++ valgrind возможные утечки в строке STL :

@ Klaim: Я не вижу, где этот документ говорит, что я не прав, но если значит, это неправильно. Чтобы процитировать стандарт C ++ (§18.3 / 8): «Автоматические объекты не уничтожаются в результате вызова exit ().» - Джеймс МакНеллис 10 сентября 2010 года в 19: 11

Поскольку выполнение «return 0» вместо «pthread_exit (0)», казалось, решило вашу проблему (и мое .. спасибо), я предполагаю, что поведение между ними одинаковое.

0 голосов
/ 02 октября 2010

У меня есть опыт, что valgrind испытывает трудности с отслеживанием хранилища, выделенного для состояния присоединяемых потоков. (Это идет в том же направлении, что указано в кафе.)

Поскольку кажется, что вы всегда возвращаете значение 0 Я полагаю, что вам, возможно, нужно объединить свои потоки с точки зрения приложения? Если это так, рассмотрите возможность запуска их отдельно от начала, это позволит избежать выделения этой памяти.

Недостатком является то, что у вас есть:

  1. создать свой собственный барьер на конец вашего main. Если вы знаете количество потоков заранее, а простой статически распределенный pthread_barrier сделает.
  2. или для выхода из вас main с pthread_exit такой, что ты не убить остальные запущенные темы это может быть еще не закончено.
0 голосов
/ 02 октября 2010

Вы действительно используете C ++, случайно?Для пояснения - ваш исходный файл заканчивается расширением .c, а вы компилируете его с gcc, а не g++?

Кажется вполне вероятным, что ваша функция выделяет ресурсы, которые вы ожидаетеочищается автоматически, когда функция возвращается.Локальные объекты C ++, такие как std::vector или std::string, делают это, и их деструкторы, вероятно, не будут запущены, если вы вызовете pthread_exit, но будут очищены, если вы просто вернетесь.

Я предпочитаю избегать низкоуровневых API, таких как pthread_exit, и всегда просто возвращаться из функции потока, где это возможно.Они эквивалентны, за исключением того, что pthread_exit является де-факто конструкцией управления потоком, которая обходит язык, который вы используете, но return не делает.

...