Exercice pour débuter sur les threads

Le problème exposé dans ce sujet a été résolu.

Bonjour,

On me demande d’écrire un programme C créant un thread. Les deux threads (principal + celui créé) devront ensuite affichent cent fois "1" s’il s’agit du thread principal et "2" s’il s’agit du thread créé. En toute logique, l’exécution étant en parallèle, il n’y a aucune raison que l’un des threads soit plus rapide que l’autre pour afficher son chiffre à l’écran. Par conséquent, on s’attend à avoir sur la sortie des "1" et des "2" mélangés au nombre total de 200.

Pourtant, on remarque que les chiffres "1" apparaissent bien avant les chiffres "2" :

Capture.PNG
Capture.PNG

Qu’ai-je mal compris ? Pourquoi tous les "1" sont avant les "2" ?

Mon code :

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

#include <pthread.h>
#include <errno.h>


void* th_sum(void* param)
{
    int* n = (int*) param;
    int* r = (int*) malloc(sizeof(int)); // On y mettra la valeur de retour du thread
    
    if (!r)
    {
        perror("Erreur allocation : ");
        exit(EXIT_FAILURE);
    }
    
    // Affiche n fois "2 " à l'écran
    for (int i = 0; i < *n; ++i)
        printf("2 ");
    
    *r = 1; // Valeur de retour : tout s'est bien passé
    return r;
}



int main()
{
    int* th_ret;
    int  nb_iteration = 500;
    
    pthread_t tid;
    int err = pthread_create(&tid, NULL, th_sum, &nb_iteration);
    
    if (err != 0)
    {
        errno = err;
        perror("Erreur thread : ");
        return EXIT_FAILURE;
    }
    
    // Affiche n fois "1 " à l'écran
    for (int i = 0; i < nb_iteration; ++i)
        printf("1 ");
    
    pthread_join(tid, (void**) &th_ret);
    printf("retour thread : %i\n", *th_ret);
    
    free(th_ret);
    return EXIT_SUCCESS;
}

Il n’est pas possible d’avoir deux programmes ou threads qui écrivent dans un même fichier en même temps (enfin, surtout ils ne peuvent pas ouvrir le fichier en écriture tous les deux). Hors le flux de sortie (que tu utilises avec printf) est en fait un fichier. Si tu fermes le fichier entre chaque écriture, tu devrais pouvoir t’en sortir. Je ne pense pas que l’on puisse fermer le flux standard de sortie, donc essaie avec un fichier externe que tu gères toi même et écrit dans ce fichier-ci en fermant le fichier à chaque itération (de même en le réouvrant à chaque itération)

Ça m’étonne ça: en effet deux programmes ne peuvent pas écrire en même temps sur un fichier mais dans mon souvenir, on avait fait qqc comme ça en cours et ça marchait très bien (d’ailleurs, je ne suis même pas sûr que les flux standards d’entrée/sortie soient gérés par le système avec des verrous). En revanche, je me demande si tu fait tourner ce ton programme directement sur ta machine réelle ou ben sur une machine virtuelle. Dans le deuxième cas, je ne suis pas sûr qu’il y ait une garantie que ta machine réelle exécute les x cœurs de ta machine virtuelle sur des cœurs différent; auquel cas, ça expliquerait le résultat.

Edit: Et puis quoi qu’il en soit, là on ne parle pas de deux programmes qui écrivent sur le même fichier en même temps, mais du même programme qui écrit sur le même flux (qu’il se comporte comme un fichier du point de vue des locks ou pas). Comme le dit renault plus bas, a part pour l’exercice, c’est pas forcément une bonne idée de faire comme ça, mais c’est possible.

+0 -0

Il n’est pas possible d’avoir deux programmes ou threads qui écrivent dans un même fichier en même temps (enfin, surtout ils ne peuvent pas ouvrir le fichier en écriture tous les deux). Hors le flux de sortie (que tu utilises avec printf) est en fait un fichier. Si tu fermes le fichier entre chaque écriture, tu devrais pouvoir t’en sortir. Je ne pense pas que l’on puisse fermer le flux standard de sortie, donc essaie avec un fichier externe que tu gères toi même et écrit dans ce fichier-ci en fermant le fichier à chaque itération (de même en le réouvrant à chaque itération)

Vanadiae

Bien sûr que si deux programmes peuvent écrire en même temps sur le même fichier. D’ailleurs j’ai bien le comportement attendu par l’auteur avec son code sur ma machine.

Mais ce comportement est évidemment à éviter pour des raisons évidentes de cohérence de la sortie ou du fichier.

Le comportement de l’auteur a plusieurs explications. La gestion d’un système multithread dépend du système d’exploitation et de la machine. S’il n’a qu’un cœur et que son système n’interrompt pas le thread 1 au profit du thread 2, on peut avoir une suite de 1 suivis d’une suite de 2 de manière légitime. Puis il y a la question du cache et en particulier du buffer de la sortie standard qui entrent en jeu ici.

Je conseillerais aussi de mettre un saut de ligne à chaque écriture pour forcer de vider le buffer et réduire l’impact du cache.

Une autre idée pour mettre en évidence ce comportement est d’augmenter la taille des boucles, 500 itérations c’est finalement peu. Plus on augmente, plus la probabilité d’avoir un changement d’exécution augmente.

Ne pas oublier que dans le cas d’un processus multithread, on ne peut pas garantir l’ordre d’exécution et le comportement associé (à moins d’avoir un système temps réel). Il faut s’attendre à tous les scénarios possibles.

+5 -0

Je conseillerais aussi de mettre un saut de ligne à chaque écriture pour forcer de vider le buffer et réduire l’impact du cache.

Renault

C’est la première chose qui m’est venue à l’esprit : dans pas mal d’implémentations, printf() attend que la chaîne qu’on lui passe contienne un retour à la ligne avant d’afficher quoi que ce soit (l’histoire du cache).

+0 -0

Salut,

Il n’est pas possible d’avoir deux programmes ou threads qui écrivent dans un même fichier en même temps (enfin, surtout ils ne peuvent pas ouvrir le fichier en écriture tous les deux). Hors le flux de sortie (que tu utilises avec printf) est en fait un fichier. Si tu fermes le fichier entre chaque écriture, tu devrais pouvoir t’en sortir. Je ne pense pas que l’on puisse fermer le flux standard de sortie, donc essaie avec un fichier externe que tu gères toi même et écrit dans ce fichier-ci en fermant le fichier à chaque itération (de même en le réouvrant à chaque itération)

Vanadiae

Pour compléter la réponse de Renault, depuis le C11, ce comportement exclusif est possible en ajoutant la lettre x au mode a ou w de fopen(). Dans un tel cas, l’ouverture échoue si le fichier existe déjà.

+0 -0

Merci pour vos retours !

Je travaille effectivement sur une machine virtuelle qui simule Linux.

En ajoutant les caractères de retour à la ligne, on a une meilleure alternance.
J’ai réussi au premier coup à avoir 250 "1" suivis de 250 "2" suivis de 250 "1" suivis de 250 "2".

1
1
1
1
...
2
2
2
2
...
1
1
1
1
...
2
2
2
2
...
retour thread : 1
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte