Согласно pthread_key_create На странице man мы можем связать деструктор, который будет вызываться при закрытии потока. Моя проблема в том, что зарегистрированная функция деструктора не вызывается. Суть моего кода заключается в следующем.
static pthread_key_t key;
static pthread_once_t tls_init_flag = PTHREAD_ONCE_INIT;
void destructor(void *t) {
// thread local data structure clean up code here, which is not getting called
}
void create_key() {
pthread_key_create(&key, destructor);
}
// This will be called from every thread
void set_thread_specific() {
ts = new ts_stack; // Thread local data structure
pthread_once(&tls_init_flag, create_key);
pthread_setspecific(key, ts);
}
Есть идеи, что может помешать этому деструктору вызвать? Я также использую atexit () в данный момент, чтобы выполнить некоторую очистку в основном потоке. Есть ли шанс, что это мешает вызову функции деструктора? Я попытался удалить это также. Все еще не работал все же. Также мне не ясно, должен ли я обрабатывать основной поток как отдельный случай с atexit. (Кстати, использовать atexit обязательно, так как мне нужно выполнить некоторую очистку приложения при выходе из приложения)
Это по замыслу.
Основной поток завершается (возвращением или вызовом exit()
), и это не использует pthread_exit()
, Документы POSIX pthread_exit
вызов деструкторов, специфичных для потока.
Вы могли бы добавить pthread_exit()
в конце main
, Кроме того, вы можете использовать atexit
сделать ваше уничтожение. В этом случае было бы чисто установить значение для потока в NULL
так что в случае pthread_exit
был вызван, разрушение не произошло бы дважды для этого ключа.
ОБНОВИТЬ На самом деле, я решил свои непосредственные проблемы, просто добавив это в мою глобальную функцию настройки модульного теста:
::atexit([] { ::pthread_exit(0); });
Итак, в контексте моего глобального класса фикстуры MyConfig
:
struct MyConfig {
MyConfig() {
GOOGLE_PROTOBUF_VERIFY_VERSION;
::atexit([] { ::pthread_exit(0); });
}
~MyConfig() { google::protobuf::ShutdownProtobufLibrary(); }
};
Некоторые из использованных ссылок:
PS. Конечно с ++ 11 введены <thread>
так что у вас есть лучшие и более портативные примитивы для работы.
Это уже в ответе Сехе, просто представить ключевые моменты в сжатой форме:
pthread_key_create()
вызовы деструктора запускаются вызовом pthread_exit()
,pthread_exit()
был вызван (т.е. вызовы деструктора инициированы).main()
возвращается, поведение как будто exit()
был вызван — вызовы деструкторов не запускаются.Это объясняется в http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html. См. Также C ++ 17 6.6.1p5 или C11 5.1.2.2.3p1.
Я написал быстрый тест, и единственное, что я изменил, было перемещение create_key
Ваш звонок за пределами set_thread_specific
,
То есть я назвал это в основном потоке.
Затем я увидел, как вызывается мой destroy при выходе из подпрограммы потока.
Я звоню деструктор () вручную в конце главный():
void * ThreadData = NULL;
if ((ThreadData = pthread_getspecific(key)) != NULL)
destructor(ThreadData);
Конечно ключ должны быть должным образом инициализированы ранее в главный() код.
PS. призвание Pthread_exit () в конце главный() кажется зависает целое приложение …
Ваша первоначальная мысль об обработке основного потока как отдельного случая с atexit работала лучше всего для меня.
Имейте в виду, что pthread_exit (0) перезаписывает значение выхода процесса. Например, следующая программа выйдет с нулевым статусом, хотя main () вернется с номером три:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
class ts_stack {
public:
ts_stack () {
printf ("init\n");
}
~ts_stack () {
printf ("done\n");
}
};
static void cleanup (void);
static pthread_key_t key;
static pthread_once_t tls_init_flag = PTHREAD_ONCE_INIT;
void destructor(void *t) {
// thread local data structure clean up code here, which is not getting called
delete (ts_stack*) t;
}
void create_key() {
pthread_key_create(&key, destructor);
atexit(cleanup);
}
// This will be called from every thread
void set_thread_specific() {
ts_stack *ts = new ts_stack (); // Thread local data structure
pthread_once(&tls_init_flag, create_key);
pthread_setspecific(key, ts);
}
static void cleanup (void) {
pthread_exit(0); // <-- Calls destructor but sets exit status to zero as a side effect!
}
int main (int argc, char *argv[]) {
set_thread_specific();
return 3; // Attempt to exit with status of 3
}