pthread_create, за которым следует pthread_detach, по-прежнему приводит к возможной потере ошибки в Valgrind - PullRequest
6 голосов
/ 31 декабря 2010

У меня проблема с Вальгриндом, который говорит мне, что у меня потеряна часть памяти:

==23205== 544 bytes in 2 blocks are possibly lost in loss record 156 of 265
==23205==    at 0x6022879: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==23205==    by 0x540E209: allocate_dtv (in /lib/ld-2.12.1.so)
==23205==    by 0x540E91D: _dl_allocate_tls (in /lib/ld-2.12.1.so)
==23205==    by 0x623068D: pthread_create@@GLIBC_2.2.5 (in /lib/libpthread-2.12.1.so)
==23205==    by 0x758D66: MTPCreateThreadPool (MTP.c:290)
==23205==    by 0x405787: main (MServer.c:317)

Код, который создает эти потоки (MTPCreateThreadPool), в основном получает индекс в блок ожидающих слотов pthread_t,и создает поток с этим.TI становится указателем на структуру, которая имеет индекс потока и pthread_t.(упрощенно / санитарно):

for (tindex = 0; tindex < NumThreads; tindex++)
  {
  int rc;
  TI = &TP->ThreadInfo[tindex];
  TI->ThreadID = tindex;

  rc = pthread_create(&TI->ThreadHandle,NULL,MTPHandleRequestsLoop,TI);
  /* check for non-success that I've omitted */
  pthread_detach(&TI->ThreadHandle);
  }

Затем у нас есть функция MTPDestroyThreadPool, которая перебирает все созданные нами потоки и отменяет их (поскольку MTPHandleRequestsLoop не завершается).

for (tindex = 0; tindex < NumThreads; tindex++)
  {
  pthread_cancel(TP->ThreadInfo[tindex].ThreadHandle);
  }

Я читал в другом месте (включая другие вопросы здесь о SO), что явное отсоединение потока предотвратит эту возможно потерянную ошибку, но это явно не так.Есть мысли?

Ответы [ 3 ]

8 голосов
/ 31 декабря 2010

Реализация потоков в glibc намеренно приводит к утечке памяти. Он хранит память, выделенную для контекста потока, в кэше для повторного использования при следующем создании потока. Я провел некоторый сравнительный анализ по сравнению с реализацией без кэширования, и кажется, что кэширование сокращает 50% от оптимального в противном случае времени для pthread_create, но резко замедляет pthread_join для чистого убытка. Конечно, это все еще (маленький) выигрыш, если вас волнует задержка создания потока, а не пропускная способность.

Также обратите внимание, что отдельному потоку очень трудно освободить свой контекст, даже если он этого хочет. С присоединяемым потоком поток, который вызывает pthread_join, может освобождать контекст, но отсоединенный поток должен был бы иметь возможность работать без стека в течение интервала между освобождением его контекста и завершением самого себя. Это может быть достигнуто только написанием этого небольшого куска кода на чистом asm.

Хотите знать, как контексты отдельных потоков возвращаются в кеш без аналогичного состояния гонки? В Linux есть функция обнуления int по определенному адресу (зарегистрированному библиотекой потоков пользовательского пространства), когда поток завершается. Таким образом, поток может безопасно добавить свой собственный контекст в кэш, поскольку до его завершения другие потоки будут по-прежнему видеть ненулевое значение (обычно его идентификатор потока) по этому адресу и интерпретировать его как означающее, что контекст все еще используется.

4 голосов
/ 31 декабря 2010

Одной из причин может быть то, что pthread_cancel на самом деле не отменяет поток - это не гарантируется. Отмена потока асинхронная; pthread_cancel возвращается немедленно, но отмена может быть отложена до следующей точки отмены. В этом случае потоки могут все еще существовать, когда Valgrind собирает статистику.

0 голосов
/ 31 декабря 2010

Создание темы, присоединяемой для ее немедленного отделения, не имеет большого смысла для меня.Это не создает для системы ничего, кроме издержек.

Я бы запустил потоки, отсоединенные от начала, у вас есть общая структура данных ThreadInfo для управления вашими потоками.

Кроме того, в вашем аргументе для каждого потока был бы флаг или что-то в этом роде ThreadInfo, указывающее потоку отключение контролируемым образом.

...