7 votes

pthreads : comment s'assurer que le code est exécuté dans un seul contexte threadé

J'écris une bibliothèque en C qui a besoin de fork() pendant l'initialisation. Par conséquent, je veux affirmer() que le code de l'application (qui est hors de mon contrôle) appelle le code d'initialisation de ma bibliothèque à partir d'un seul contexte threadé (pour éviter le fameux " les fils et la fourchette ne se mélangent pas "). Une fois que ma bibliothèque a été initialisée, elle est thread safe (et il est prévu que le code au niveau de l'application puisse créer des threads). Je ne suis concerné que par la prise en charge des pthreads.

Il semble impossible de compter le nombre de threads dans l'espace du processus actuel en utilisant pthreads. En effet, même googletest n'implémente que GetThreadCount() sur Mac OS et QNX.

Étant donné que je ne peux pas compter les fils, est-il possible que je puisse affirmer un contexte à fil unique ?

Clarification : Si possible, je voudrais éviter d'utiliser "/proc" (non portable), une dépendance supplémentaire de la bibliothèque (comme libproc) et les wrappers pthread_create de style LD_PRELOAD.

Clarification #2 : Dans mon cas, l'utilisation de plusieurs processus est nécessaire car les travailleurs de ma bibliothèque sont relativement lourds (utilisant webkit) et peuvent se planter. Cependant, je veux que le processus original survive aux plantages des travailleurs.

2voto

Nominal Animal Points 7207

Vous pouvez marquer la fonction d'initialisation de votre bibliothèque pour qu'elle soit exécutée avant l'application. main() . Par exemple, en utilisant GCC,

static void my_lib_init(void) __attribute__((constructor));

static void my_lib_init(void)
{
    /* ... */
}

Une autre option consiste à utiliser posix_spawn() pour bifurquer et exécuter les processus de travail en tant que binaires séparés, esclaves.

ÉDIFIÉ POUR AJOUTER :

Il me semble que si vous souhaitez déterminer si le processus a déjà créé des threads (réels, basés sur le noyau), vous devrez vous appuyer sur un code spécifique au système d'exploitation.

Dans le cas de Linux, la détermination est simple, et peut être exécutée sans danger sur d'autres systèmes d'exploitation également. Si elle ne peut pas déterminer le nombre de threads utilisés par le processus actuel, la fonction renvoie -1 :

#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

int count_threads_linux(void)
{
    DIR           *dir;
    struct dirent *ent;
    int            count = 0;

    dir = opendir("/proc/self/task/");
    if (!dir)
        return -1;

    while (1) {

        errno = 0;
        ent = readdir(dir);
        if (!ent)
            break;

        if (ent->d_name[0] != '.')
            count++;
    }

    if (errno) {
        const int saved_errno = errno;
        closedir(dir);
        errno = saved_errno;
        return -1;
    }

    if (closedir(dir))
        return -1;

    return count;
}

Il y a certains cas (comme chroot sans /proc/ ) alors que cette vérification échouera même sous Linux, de sorte que l'option -1 La valeur de retour doit toujours être traitée comme inconnu plutôt que erreur (bien que errno indiquera la raison réelle de l'échec).

En regardant les pages de manuel de FreeBSD, je me demande si les informations correspondantes sont disponibles.

Enfin :

Plutôt que d'essayer de détecter le cas problématique, je vous recommande sérieusement de fork() y exec() (ou posix_spawn() ) les processus esclaves, en n'utilisant que des fonctions asynchrones sécurisées par des signaux (cf. man 7 signal ) dans le processus enfant (avant exec() ), évitant ainsi les complications liées au fork()-thread. Vous pouvez toujours créer des segments de mémoire partagée, des paires de sockets, etc. avant de forker(). Le seul inconvénient que je vois est que vous devez utiliser des binaires séparés pour les travailleurs esclaves. Ce qui, étant donné la description que vous en faites, ne me semble pas être un inconvénient.

1voto

Arun Taylor Points 1144

Si vous envoyez un signal SIGINFO au tty de contrôle du processus, ce dernier devrait décrire l'état des threads. A partir de cette description, il devrait être possible de déduire si des threads ont été créés.

Vous devrez peut-être développer un petit utilitaire qui sera invoqué via popen pour relire la sortie dans votre bibliothèque.

Ajouté un exemple de code Fri Dec 21 14:45

Exécutez un programme simple qui crée cinq fils. En principe, les threads dorment. Avant que le programme ne se termine, envoyez un signal SIGINFO pour obtenir l'état des threads.

openbsd> cat a.c
#include <unistd.h>
#include <pthread.h> 

#define THREADS 5

void foo(void);

int
main()
{    
    pthread_t thr[THREADS];
    int j;

    for (j = 0; j < THREADS; j++) {
        pthread_create(&thr[j], NULL, (void *)foo, NULL);
    }   

    sleep(200);                             

    return(0);
}             

void
foo()
{   
    sleep(100);
}             
openbsd> gcc a.c -pthread
openbsd> a.out &
[1] 1234
openbsd> kill -SIGINFO 1234
 0x8bb0e000 sleep_wait  15 -c---W---f 0000          
 0x8bb0e800 sleep_wait  15 -c---W---f 0000          
 0x8bb0e400 sleep_wait  15 -c---W---f 0000          
 0x7cd3d800 sleep_wait  15 -c---W---f 0000          
 0x7cd3d400 sleep_wait  15 -c---W---f 0000          
 0x7cd3d000 sleep_wait  15 -c---W---f 0000 main

-1voto

ydroneaud Points 1960

Vous pourriez utiliser pthread_once() pour s'assurer qu'aucun autre thread ne fait la même chose : de cette façon, vous n'avez pas à vous soucier du fait que plusieurs threads appellent votre fonction d'initialisation, un seul sera réellement exécuté.

Faites en sorte que votre fonction d'initialisation publique exécute une initialisation privée par l'intermédiaire de pthread_once() .

static pthread_once_t my_initialisation_once = PTHREAD_ONCE_INIT;

static void my_initialisation(void)
{
    /* do something */
}

int lib_initialisation(void)
{
    pthread_once(&my_initialisation_conce, my_initialisation);

    return 0;
}

Un autre exemple peut être trouvé aquí .

Liens

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X