Как вы запрашиваете pthread, чтобы увидеть, если он все еще работает? - PullRequest
49 голосов
/ 28 января 2010

В моем деструкторе я хочу аккуратно уничтожить поток.

Моя цель - дождаться окончания выполнения потока и затем уничтожить поток.

Единственное, что я нашел в запросесостояние pthread: pthread_attr_setdetachstate , но это говорит только о том, что ваш поток:

  • PTHREAD_CREATE_DETACHED
  • PTHREAD_CREATE_JOINABLE
1013 *1013*из них не имеет ничего общего с тем, работает ли поток или нет.

Как запросить pthread, чтобы убедиться, что он все еще работает?

Ответы [ 7 ]

38 голосов
/ 28 января 2010

Похоже, у вас есть два вопроса здесь:

Как я могу дождаться завершения моей темы?

Ответ: Это напрямую поддерживается pthreads - сделайте ваш поток остановленным JOINABLE (при его первом запуске) и используйте pthread_join (), чтобы заблокировать ваш текущий поток, пока поток не будет остановлен больше не работает.


Как узнать, работает ли еще мой поток?

Ответ: Вы можете добавить флаг "thread_complete", чтобы сделать трюк:

Сценарий: Поток A хочет знать, жив ли Поток B.

Когда создается поток B, ему дается указатель на адрес флага "thread_complete". Флаг "thread_complete" должен быть инициализирован как NOT_COMPLETED до создания потока. Функция точки входа потока B должна немедленно вызвать pthread_cleanup_push (), чтобы выдвинуть «обработчик очистки», который устанавливает флаг «thread_complete» в значение COMPLETED.

Подробнее об обработчиках очистки см. Здесь: Обработчики очистки pthread

Вы захотите включить соответствующий вызов pthread_cleanup_pop (1), чтобы гарантировать, что обработчик очистки будет вызываться независимо от того, что (т. Е. Если поток завершается нормально ИЛИ из-за отмены и т.

Затем Поток A может просто проверить флаг "thread_complete", чтобы увидеть, завершился ли Поток B.

ПРИМЕЧАНИЕ. Ваш флаг "thread_complete" должен быть объявлен как "volatile" и должен быть атомарного типа - для этой цели компиляторы GNU предоставляют sig_atomic_t. Это позволяет двум потокам согласованно обращаться к одним и тем же данным без необходимости использования конструкций синхронизации (мьютексов / семафоров).

30 голосов
/ 26 марта 2012
pthread_kill(tid, 0);

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

ВНИМАНИЕ : Этот ответ неверный. Стандарт специально запрещает передачу идентификатора потока, срок службы которого истек. Этот идентификатор теперь может указывать на другой поток или, что еще хуже, он может ссылаться на освобожденную память, что вызывает сбой.

8 голосов
/ 28 января 2010

Я думаю, что все, что вам действительно нужно, это вызвать pthread_join (). Этот вызов не вернется, пока не завершится поток.

Если вы хотите опрашивать только для того, чтобы увидеть, работает ли еще поток или нет (и обратите внимание, что обычно это не то, что вы должны делать!), Вы можете сделать так, чтобы поток установил логическое значение volatile равным false как раз перед этим. выходит ... тогда ваш основной поток может прочитать логическое значение, и если оно все еще верно, вы знаете, что поток все еще работает. (если это false, с другой стороны, вы знаете, что поток, по крайней мере, почти завершен; возможно, он все еще выполняет код очистки, который происходит после того, как он устанавливает логическое значение в false, хотя, даже в этом случае вы все равно должны вызывать pthread_join до пытается освободить любые ресурсы, к которым у потока может быть доступ)

5 голосов
/ 28 января 2010

Существует не полностью переносимое решение, посмотрите, поддерживает ли ваша платформа pthread_tryjoin_np или pthread_timedjoin_np. Таким образом, вы просто проверяете, можно ли присоединиться к потоку (конечно, создан с помощью PTHREAD_CREATE_JOINABLE).

0 голосов
/ 01 марта 2019

Я считаю, что я нашел решение, которое по крайней мере работает для Linux. Всякий раз, когда я создаю поток, я сохраняю его LWP (легкий процесс ID) и присваиваю ему уникальное имя, например. int lwp = syscall (SYS_gettid); prctl (PR_SET_NAME, (длинное) "уникальное имя", 0, 0, 0);

Затем, чтобы проверить, существует ли тема позже, я открываю / proc / pid / task / lwp / comm и читаю Это. Если файл существует и его содержимое соответствует уникальному имени, которое я назначил, поток существует. Обратите внимание, что это НЕ передает возможный неиспользуемый / повторно используемый TID какой-либо библиотечной функции, поэтому никаких сбоев.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/file.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <syscall.h>

pthread_t subthread_tid;
int       subthread_lwp;

#define UNIQUE_NAME "unique name"

bool thread_exists (pthread_t thread_id)
{
    char path[100];
    char thread_name[16];
    FILE *fp;
    bool  thread_exists = false;

    // If the /proc/<pid>/task/<lwp>/comm file exists and it's contents match the "unique name" the
    // thread exists, and it's the original thread (TID has NOT been reused).

    sprintf(path, "/proc/%d/task/%d/comm", getpid(), subthread_lwp);

    fp = fopen(path, "r");

    if( fp != NULL ) {

        fgets(thread_name, 16, fp);
        fclose(fp);

        // Need to trim off the newline
        thread_name[strlen(thread_name)-1] = '\0';

        if( strcmp(UNIQUE_NAME, thread_name) == 0 ) {
            thread_exists = true;
        }
    }

    if( thread_exists ) {
        printf("thread exists\n");
    } else {
        printf("thread does NOT exist\n");
    }

    return thread_exists;
}


void *subthread (void *unused)
{
    subthread_lwp = syscall(SYS_gettid);
    prctl(PR_SET_NAME, (long)UNIQUE_NAME, 0, 0, 0);

    sleep(10000);

    return NULL;
}


int main (int argc, char *argv[], char *envp[])
{
    int error_number;

    pthread_create(&subthread_tid, NULL, subthread, NULL);
    printf("pthread_create()\n");
    sleep(1);
    thread_exists(subthread_tid);

    pthread_cancel(subthread_tid);
    printf("pthread_cancel()\n");
    sleep(1);
    thread_exists(subthread_tid);

    error_number = pthread_join(subthread_tid, NULL);
    if( error_number == 0 ) {
        printf("pthread_join() successful\n");
    } else {
        printf("pthread_join() failed, %d\n", error_number);
    }
    thread_exists(subthread_tid);

    exit(0);
}
0 голосов
/ 26 сентября 2016

Позвольте мне отметить «выигрышный» ответ, который имеет огромный скрытый недостаток и в некоторых случаях может привести к сбоям. Если вы не используете pthread_join, он будет появляться снова и снова. Предположим, у вас есть процесс и общая библиотека. Позвоните в библиотеку lib.so.

  1. Вы добавляете это, вы начинаете нить в нем. Предположим, вы не хотите, чтобы он присоединялся к нему, поэтому установите его на отрыв.
  2. Процесс и логика разделяемой библиотеки, выполняющие свою работу, и т. Д. *
  3. Вы хотите загрузить lib.so, потому что он вам больше не нужен.
  4. Вы вызываете завершение работы потока и говорите, что хотите потом прочитать флаг из потока вашего lib.so, чтобы он завершился.
  5. Вы продолжаете в другом потоке с помощью dlclose, потому что вы видите, что вы видели, что флаг теперь показывает поток как "завершенный"
  6. dlclose загрузит всю память, связанную со стеком и кодом.
  7. Опа, но dlclose не останавливает темы. И вы знаете, даже когда вы находитесь в последней строке обработчика очистки для установки изменяемой переменной атомарного флага «поток завершен», вам все равно придется возвращаться из множества методов в стеке, возвращая значения и т. Д. Если Огромный приоритет потока был отдан потоку # 5 + # 6, вы получите dlclose до того, как ДЕЙСТВИТЕЛЬНО остановитесь в потоке. Иногда у вас будут хорошие сбои.

Позвольте мне указать, что это не гипотетическая проблема, у меня была такая же проблема в нашем проекте.

0 голосов
/ 24 октября 2015
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>

void* thread1 (void* arg);
void* thread2 (void* arg);

int main()
{
    pthread_t thr_id;

    pthread_create(&thr_id, NULL, thread1, NULL);

    sleep(10);
}

void* thread1 (void* arg)
{
    pthread_t thr_id = 0;

    pthread_create(&thr_id, NULL, thread2, NULL);

    sleep(5);
    int ret = 0;
    if( (ret = pthread_kill(thr_id, 0)) == 0)
    {
        printf("still running\n");
        pthread_join(thr_id, NULL);
    }
    else
    {
        printf("RIP Thread = %d\n",ret);
    }
}

void* thread2 (void* arg)
{
//  sleep(5);
    printf("I am done\n");
}
...