Question

J'essaie de partager une structure entre deux threads qui n'est pas une variable globale. La variable elle-même est instanciée sur la pile dans la fonction principale, puis son pointeur est passé en tant que paramètre aux deux threads lors du démarrage des deux threads.

Ce que je constate, c'est que lorsque je modifie la valeur d'un membre de cette structure, ce changement n'est pas répercuté jamais dans l'autre titre. Existe-t-il un moyen de partager une variable non globale (par exemple un entier) entre deux threads, de sorte que les modifications apportées à cette variable dans un thread apparaissent dans l'autre?

C’est parce que je veux éviter d’ajouter des variables globales pour la maintenabilité du code.

Était-ce utile?

La solution

N'oubliez pas que les threads ont leur propre pile. Par conséquent, si vous transmettez un entier et que la fonction sort du cadre, vous faites référence à la mémoire qui contient maintenant quelque chose de complètement différent.

void foo(void)
{
    pthread_t t1, t2;
    struct foo common_value;

    if (pthread_create(&t1, NULL, a, &common_value) != 0)
    {
        return -1;
    }    

    if (pthread_create(&t2, NULL, b, &common_value) != 0)
    {
        return -1;
    }    

    // upon exiting this function, common_value is no longer what you think it is!
}

Autres conseils

Je ne prétends pas élégance, mais cela semble fonctionner pour moi sous MacOS X 10.5.8.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void *writer(void *arg)
{
    int * volatile i = arg;

    for (*i = 1; *i < 10; (*i)++)
    {
        printf("writer(): pseudo_global = %d\n", *i);
        fflush(stdout);
        sleep(1);
    }
    printf("writer(): pseudo_global = %d (exiting)\n", *i);
    fflush(stdout);
    return(0);
}

static void *reader(void *arg)
{
    int * volatile i = arg;
    while (*i < 10)
    {
        printf("reader(): pseudo_global = %d\n", *i);
        fflush(stdout);
        sleep(1);
    }
    printf("reader(): pseudo_global = %d (exiting)\n", *i);
    fflush(stdout);
    exit(0);
}

int main(void)
{
    volatile int pseudo_global = 0;
    pthread_t t1;
    pthread_t t2;
    if (pthread_create(&t1, 0, writer, &pseudo_global) != 0)
    {
        perror("pthread_create() for thread 1");
        exit(1);
    }
    if (pthread_create(&t2, 0, reader, &pseudo_global) != 0)
    {
        perror("pthread_create() for thread 1");
        exit(1);
    }
    while (pseudo_global < 10)
    {
        printf("main():   pseudo_global = %d\n", pseudo_global);
        fflush(stdout);
        sleep(1);
    }
    printf("main():   pseudo_global = %d (exiting)\n", pseudo_global);
    fflush(stdout);
    return(0);
}

Notez que j'ai mis le "qualificatif" volatile dans le code sur "assurez-vous", mais à part le fait de générer des avertissements concernant les qualificateurs ignorés dans les appels à pthread_create () , aucun différence significative. J'ai également exécuté le code sans aucun qualificateur volatile sans problème.

Ce code démontre, je pense, que sur au moins une implémentation de threads POSIX, vous pouvez effectivement partager une variable locale sur la pile d'une fonction qui ne s'est pas terminée pendant l'exécution des threads.

Je suis prêt à croire que je devrais faire plus attention à la terminaison du thread et que je devrais m'assurer que main () ne se ferme pas sans utiliser pthread_join () pour garantir que les threads sont sortis en premier.

Exemple de sortie:

$ make ptex
gcc -O ptex.c -o ptex  
ptex.c: In function ‘main’:
ptex.c:40: warning: passing argument 4 of ‘pthread_create’ discards qualifiers from pointer target type
ptex.c:45: warning: passing argument 4 of ‘pthread_create’ discards qualifiers from pointer target type
$ ./ptex
writer(): pseudo_global = 1
main():   pseudo_global = 0
reader(): pseudo_global = 1
writer(): pseudo_global = 2
main():   pseudo_global = 2
reader(): pseudo_global = 2
writer(): pseudo_global = 3
main():   pseudo_global = 3
reader(): pseudo_global = 3
writer(): pseudo_global = 4
main():   pseudo_global = 4
reader(): pseudo_global = 4
writer(): pseudo_global = 5
reader(): pseudo_global = 5
main():   pseudo_global = 5
writer(): pseudo_global = 6
reader(): pseudo_global = 6
main():   pseudo_global = 6
writer(): pseudo_global = 7
reader(): pseudo_global = 7
main():   pseudo_global = 7
writer(): pseudo_global = 8
reader(): pseudo_global = 8
main():   pseudo_global = 8
writer(): pseudo_global = 9
reader(): pseudo_global = 9
main():   pseudo_global = 9
writer(): pseudo_global = 10 (exiting)
reader(): pseudo_global = 10 (exiting)
main():   pseudo_global = 10 (exiting)
$

Vous faites le bon choix, mais votre expérience suggère que vous le faites mal. Essayez de le déplacer dans une variable globale et voyez si vous continuez à ne pas partager.

Une fois que vous avez coché cette case, publiez un exemple de code simple, illustrant votre problème, que d'autres peuvent tester.

À mon avis, partager une variable de pile est pire que d'avoir une variable globale. À tout le moins, j'allouerais de la mémoire sur le tas à partager entre les threads. Si la mémoire est censée vivre pendant toute la durée du programme, vous n'avez même pas à vous soucier de le libérer et le thread principal peut revenir immédiatement. Avec la variable de pile, vous devez vous assurer que vous n’écrasez pas prématurément le cadre de la pile où se trouve la variable.

Cela dit, je pense que l’utilisation d’un système global est en fait la meilleure solution. Placez le global dans un fichier séparé et déclarez-le static afin qu'il ne soit global que pour le fichier. Ensuite, placez uniquement les fonctions de votre fil dans ce fichier afin qu’elles aient accès à la variable. J'aime mieux cela car il est explicite que la mémoire est partagée par les threads. L'argument de pthread_create est vraiment censé être un contexte spécifique à un thread, et il ne sera pas nécessairement évident de regarder les fonctions de thread que tous les threads reçoivent le même pointeur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top