Jak odpytywać pthread, aby sprawdzić, czy nadal działa?

W moim destruktorze chcę zniszczyć nić czysto.

Moim celem jest oczekiwanie na zakończenie wykonania wątku, a następnie zniszczenie wątku.

Jedyną rzeczą, którą znalazłem o zapytaniu stanu pthread jest pthread_attr_setdetachstate ale to mówi tylko, jeśli Twój wątek jest:

  • PTHREAD_CREATE_DETACHED
  • PTHREAD_CREATE_JOINABLE

Oba nie mają nic wspólnego z tym, czy wątek nadal działa, czy nie.

Jak odpytywać pthread, aby sprawdzić, czy nadal działa?

Author: Trevor Boyd Smith, 2010-01-28

7 answers

Wygląda na to, że masz tu dwa pytania:

Jak mogę poczekać, aż mój wątek się zakończy?

Odpowiedź: jest to bezpośrednio obsługiwane przez pthreads -- spraw, aby wątek do zatrzymania był łączony (gdy jest uruchamiany po raz pierwszy) i użyj pthread_join (), aby zablokować bieżący wątek, dopóki wątek do zatrzymania nie przestanie działać.


Jak mogę sprawdzić, czy mój wątek nadal działa?

Odpowiedź: możesz dodać flagę "thread_complete", aby zrobić sztuczkę:

Scenariusz: Wątek A chce wiedzieć, czy wątek B nadal żyje.

Gdy wątek B jest tworzony, otrzymuje wskaźnik do adresu znacznika "thread_complete". Znacznik "thread_complete" powinien zostać zainicjowany na NOT_COMPLETED przed utworzeniem wątku. Funkcja punktu wejścia wątku B powinna natychmiast wywołać pthread_cleanup_push (), aby nacisnąć "Cleanup handler", który ustawia znacznik "thread_complete" jako zakończony.

Zobacz szczegóły na temat obsługi sprzątania tutaj: pthread cleanup handlers

Będziesz chciał dołączyć odpowiednie wywołanie pthread_cleanup_pop(1), aby upewnić się, że obsługa czyszczenia zostanie wywołana bez względu na wszystko (np. jeśli wątek zakończy się normalnie lub z powodu anulowania, itp.).

Następnie wątek a może po prostu sprawdzić flagę "thread_complete", aby sprawdzić, czy wątek B zakończył się.

Uwaga: twój znacznik " thread_complete "powinien być zadeklarowany jako" volatile " i powinien być typem atomowym -- Kompilatory GNU dostarczają do tego celu sig_atomic_t. Pozwala to dwóm wątkom spójnym uzyskać dostęp do tych samych danych bez potrzeby budowy synchronizacji (muteksy/semafory).

 42
Author: jeremytrimble,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-28 17:39:20
pthread_kill(tid, 0);

Nie jest wysyłany żaden sygnał, ale sprawdzanie błędów jest nadal wykonywane, więc możesz go użyć do sprawdzenia istnienie tid.

Uwaga : ta odpowiedź jest niepoprawna. Standard wyraźnie zabrania przekazywania identyfikatora wątku, którego żywotność została zakończona. Ten identyfikator może teraz określać inny wątek lub, co gorsza, może odnosić się do pamięci, która została zwolniona, powodując awarię.

 29
Author: pthread_kill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-26 11:26:19

Myślę, że wszystko, czego naprawdę potrzebujesz, to wywołanie pthread_join (). To połączenie nie wróci, dopóki wątek się nie skończy.

Jeśli chcesz tylko sprawdzić, czy wątek nadal działa ,czy nie (i pamiętaj, że zwykle nie jest to, co powinieneś chcieć zrobić!), możesz mieć wątek ustawiony zmienną logiczną na false tuż przed jego zakończeniem... wtedy twój główny wątek może odczytać wartość logiczną i jeśli nadal jest to prawda, wiesz, że wątek nadal działa. (jeśli jest to fałszywe, z drugiej strony, Ty wiedz, że wątek jest co najmniej prawie skończony; nadal może być uruchomiony kod czyszczenia, który występuje po ustawieniu logiki na false, więc nawet w tym przypadku powinieneś nadal wywoływać pthread_join przed próbą uwolnienia zasobów, do których wątek może mieć dostęp)

 8
Author: Jeremy Friesner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-28 17:32:35

Nie ma w pełni przenośnego rozwiązania, sprawdź, czy Twoja platforma obsługuje pthread_tryjoin_np lub pthread_timedjoin_np. Więc wystarczy sprawdzić, czy wątek może być połączony (oczywiście utworzony za pomocą PTHREAD_CREATE_JOINABLE).

 5
Author: Dewfy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-28 17:13:30

Pozwolę sobie zwrócić uwagę na" zwycięską " odpowiedź, która ma ogromną ukrytą wadę, a w niektórych kontekstach może prowadzić do awarii. Jeśli nie użyjesz pthread_join, będzie się pojawiać ponownie i ponownie. Załóżmy, że masz proces i bibliotekę współdzieloną. Zadzwoń do biblioteki lib.so.

  1. otwierasz go, zaczynasz w nim wątek. Załóżmy, że nie chcesz, aby się do niego przyłączył, więc ustaw go odłączalny.
  2. proces i współdzielenie logiki Liba wykonującego swoją pracę, itp...
  3. Chcesz załadować lib.so, bo ty już go nie potrzebuję. Wywołujesz zamknięcie wątku i mówisz, że chcesz później odczytać flagę ze swojego lib.so ' s thread, that it have finished.
  4. kontynuujesz kolejny wątek z dlclose, ponieważ widzisz, że widziałeś, że flaga pokazuje teraz wątek jako "Zakończony"
  5. dlclose załaduje całą pamięć związaną ze stosem i kodem.
  6. Whops, ale dlclose nie zatrzymuje wątków. I wiesz, nawet gdy jesteś w ostatniej linii sprzątania handler aby ustawić zmienną Atomic FLAG" thread is finished", musisz wrócić z wielu metod na stosie, zwracając wartości itp. Jeśli Dla wątku #5+#6 został nadany ogromny priorytet wątku, otrzymasz dlclose, zanim będziesz mógł naprawdę zatrzymać wątek. Czasami będziesz miał niezłe awarie.

Pozwolę sobie zauważyć, że nie jest to problem hipotetyczny, miałem ten sam problem w naszym projekcie.

 0
Author: newhouse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-26 11:20:55

Wydaje mi się, że wymyśliłem rozwiązanie, które przynajmniej działa na Linuksa. Ilekroć tworzę wątek mam go zapisać to LWP (Light Weight process ID) i przypisać mu unikalną nazwę, np. int lwp = syscall (SYS_gettid); prctl (PR_SET_NAME, (long) "unikalna nazwa", 0, 0, 0);

Następnie, aby sprawdzić czy wątek istnieje później otwieram / proc/pid/zadanie/lwp/komunikator i przeczytaj. Jeśli plik istnieje i jego zawartość pasuje do przypisanej mi unikalnej nazwy, wątek istnieje. Zauważ, że to nie przekazuje nieistniejącego/ponownie użytego identyfikatora TID do żadnej funkcji biblioteki, więc nie ulega awarii.

#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
Author: Eric Buell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-01 01:39:19
#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");
}
 -1
Author: Priyesh Shah Pilu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-23 23:22:45