CONNEXION
  • RetourJeux
    • Tests
    • Soluces
    • Previews
    • Sorties
    • Hit Parade
    • Les + attendus
    • Tous les Jeux
  • RetourActu
    • Culture Geek
    • Astuces
    • Réalité Virtuelle
    • Rétrogaming
    • Toutes les actus
  • RetourHigh-Tech
    • Actus JVTECH
    • Bons plans
    • Tutoriels
    • Tests produits High-Tech
    • Guides d'achat High-Tech
    • JVTECH
  • RetourVidéos
    • A la une
    • Gaming Live
    • Vidéos Tests
    • Vidéos Previews
    • Gameplay
    • Trailers
    • Chroniques
    • Replay Web TV
    • Toutes les vidéos
  • RetourForums
    • Hardware PC
    • PS5
    • Switch
    • Xbox Series
    • Overwatch 2
    • FUT 23
    • League of Legends
    • Genshin Impact
    • Tous les Forums
  • PC
  • PS5
  • Xbox Series
  • PS4
  • One
  • Switch
  • Wii U
  • iOS
  • Android
  • MMO
  • RPG
  • FPS
En ce moment Genshin Impact Valhalla Breath of the wild Animal Crossing GTA 5 Red dead 2
Etoile Abonnement RSS

Sujet : [C] Est-ce que ces threads s'executent en même temps ?

DébutPage précedente
12
Page suivanteFin
Calistas Calistas
MP
Niveau 10
26 septembre 2016 à 20:18:50

Bonjour, j'ai essayé de faire un programme en programmation parallèle où genre je lance 2 threads de la même fonction avec un paramètre différent mais est-ce qu'ils s'executent en même temps de la façon dont je l'ai écrit ?

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

void    *monthread(void *arg)
{
	char c;
	int r;
	printf("Thread is in execution, fuck me rightt\n"); // this line is executed ! why not the others ?
	c = getchar();
	scanf("%d", &r);
	while(1)
	{
		//c = 'e';
		printf("loop %d\n", r);
	}

	(void) arg; // and can someone explain me this line please ? i was reading a tutorial
	//pthread_exit(NULL); // and it says to add this but why ? what does it do  ?  thanks
}

int main(void){
	int i;
	pthread_t thread_h;
	pthread_t thread_h2;
	printf("Thread creation in 3 2 1\n");

	if(pthread_create(&thread_h, NULL, monthread, NULL) == -1){
		perror("pthread_create");
		return EXIT_FAILURE;
	}
	
	if(pthread_create(&thread_h2, NULL, monthread, NULL) == -1){
		perror("pthread_create");
		return EXIT_FAILURE;
	}

	pthread_join(thread_h, NULL);
	pthread_join(thread_h2, NULL);
	//i = getchar();
	printf("thread created\n");
	/*while (1){
	  i = 0;
	  }*/
}

genre là j'ai l'affichage que du paramètre de mon 2ème thread :( c'est normal ??

godrik godrik
MP
Niveau 22
26 septembre 2016 à 20:40:05

calistas, comme d'hab avec les threads et les process, ils s'executent quand le systeme a bien envie de les executer. Ils peuvent tourner en meme temps ou ne pas tourner en meme temps. C'est le systeme qui choisit ce qu'il a envie de faire.

Dans ton cas, les deux threads peuvent tourner en meme temps. Faire des io sur stdin dans des threads different, ca ressemble a une tres mauvaise idee.

pthread exit ca ne sert a rien.
le cast en void de arg, c'est juste un truc debile pour retirer un warning du compilateur.

C'est quoi ce tuto que tu lis que je tiennes mes etudiants loin de lui ?

Calistas Calistas
MP
Niveau 10
26 septembre 2016 à 22:37:15

aucun j'ai juste lu le tuto sur les threads mais c'était pas POUR faire du parallélisme, j'ai juste essayé d'en faire un moi-même

j'ai remarqué que tu aides souvent très peu dans tes réponses et tu te moques surtout beaucoup, comme si tu étais né en sachant programmer, je te blackliste donc car à chaque fois que je crois avoir une réponse je revois encore ton pseudo, répondant inlassablement par un message qui m'est au final absolument inutile

zbou-le-boss zbou-le-boss
MP
Niveau 10
26 septembre 2016 à 23:45:00

Et si tu enlèves ton c=getchar et là ligne suivante, ça fonctionne ? Je ne peux pas tester là, et je n'ai pas fait énormément de threads en c avec lpthread.
Car au final, tant que tu n'entres pas de caractère tu ne peux pas accéder à ton while.
Tes deux threads se lancent super vite, tu en as donc probablement un qui bloque sur cette instruction, et comme les threads ça fonctionne au bon vouloir de l'ordonnanceur, tu ne sais jamais vraiment à quel thread tu as affaire dans ce genre de cas.
Essayes de mettre un sleep entre le lancement de tes threads et la lecture de ton caractère sinon, ou pose des locks.

Sinon si tu veux savoir si les deux threads tournent simultanément, lance un truc bidon genre le gestionnaire des tâches windows et regarde la charge de tes coeurs. Ca marchera, sauf si ton thread bloque, bien sûr :)

+ pour ta réponse à Godrik, ce n'est pas pour le défendre mais au moins il pousse à chercher des solutions en donnant des indices. C'est sûr, c'est moins rapide que de faire des c/c de stackoverflow mais au moins ça aide à éviter des problèmes similaires à l'avenir.

godrik godrik
MP
Niveau 22
27 septembre 2016 à 00:40:01

calistas, je ne comprends pas bien ton message. J'ai l'impression que mon message precedent repond exactement aux questions poses:

-tu demandes si les threasd tournent en meme temps. Je reponds que ca depend du systeme, mais qu'ils peuvent tourner en meme temps.

-le code demande des explicatins sur "pthread_exit(NULL);", je reponds que ca sert a rien.

-le code demande des explications sur le cas "(void) args;", je reponds que c'est un truc pour contourner un warning du compilateur.

-J'indique que tes threads font des io sur stdin et que c'est une mauvaise idee. Comme tu n'as pas l'air de bien connaitre l'utilisation des threads, j'ai pas voulu rentrer dans les details. Mais la "thread safety" des io sur un seul descripteur de fichier n'a pas ete clair pendant longtemps. Ca a ete implementation dependant pendant TRES longtemps, et ca n'a ete clarifie que recement.

-Ton code mentionne un tutorial, et visiblement tu n'arrives pas a le suivre. Donc le tuto ne doit pas etre clair. Est ce que ce n'est pas raisonnable de demander lequel tu suis afin de ne pas le recommender a l'avenir?

J'ai repondu a toutes les question pose sauf une. Pourquoi est ce que ce message n'est utile ?

FreddyCouscous FreddyCouscous
MP
Niveau 10
27 septembre 2016 à 10:35:49

Comme tout le monde l'a dit : (void) arg; sert à retirer un warning compilateur.

Pourquoi ? Car ton thread en entrée possède un argument : void *arg, mais il n'est pas utilisé dans le thread donc le compilateur va te sortir un message du style "attention, tu as une variable initialisée mais non utilisée".

pthread_exit(NULL); :d) Aucune idée.

Sinon je vois que tu utilises la fonction "getchar()". Cette fonction est bloquante dans le sens où elle va attendre qu'un utilisateur tape quelque chose dans la fenêtre de commande.

Si tu as tes deux threads qui tournent en parallèle et qui tentent d'accéder à la même ressource ça va poser des problèmes. Dans ce cas là il vaut mieux utiliser des semaphores.

Pseudo supprimé
Niveau 6
27 septembre 2016 à 15:50:15

Les opérations sur les fichiers sont atomiques mais printf utilise probablement un tampon qui n'a pas de verrou.

Ta lit la position du tampon
Tb lit la position du tampon

 * Ta et Tb ont lu la même position *

Ta incrémente la position du tampon
Tb incrémente la position du tampon

Ta écrit dans le tampon
Tb écrit dans le tampon a la même position et efface ce que Ta écrit.

Un solution serait d’utilisé sprintf dans un tampon propre a chaque thread et d'ensuite utiliser fwrite avec stdout


int length = sprintf(NULL, "loop %d\n", r);
char buffer[length];
sprintf(buffer, "loop %d\n", r);
fwrite(buffer, 1, length, stdout); // atomique
lokilok lokilok
MP
Niveau 10
27 septembre 2016 à 18:18:15

Je m'incruste pour poser une question, il y a un truc que je suis pas sur d'avoir compris avec les opérations atomique, qu'est ce qui empêche deux opérations atomiques d'être exécutées en même temps via deux threads différents sur deux cœurs différents ?

Dans un système avec un seul cœur je comprends, l'opération est effectuée d'un trait donc elle sera forcément terminée avant que les autres opérations soient exécutées, mais dans le cas d'un système possédant plusieurs cœurs je comprends pas trop comment ça se passe.

EDIT: Après quand je parle d'opérations atomiques personnellement je parle surtout des std::atomic dans la std lib du C++.

Message édité le 27 septembre 2016 à 18:20:57 par lokilok
godrik godrik
MP
Niveau 22
27 septembre 2016 à 20:23:36

(Ah, bah tiens, j'enseignais ca la semaine derniere.)
lokilok, ca depend de ce que tu appelle atomic. Mais en l'occurence tu parles des std::atomic de C++.

les atomic de C++ mappent a des instructions du processeurs qui font exactement ca. Il y a des instructions comme compare_and_swap(A,oldval, newval) qui assurent de faire atomiquement:
{ bool cnd = (*A == oldval); if (cnd) *A = newval; return cnd; }

Le truc est que l'instruction est encode directement dans le processeur. Donc le processeur garantie l'atomicite.
Si tu es plus curieux, les processeur intel garantissent l'atomicite de l'operation en mettant un genre de lock sur une ligne de cache. qui garanti que le core a un access exclusif.

https://en.wikipedia.org/wiki/Test-and-set
https://en.wikipedia.org/wiki/Compare-and-swap
https://en.wikipedia.org/wiki/Fetch-and-add

Dans les atomic C++ tu as aussi des histores de consistence memoire qui sont garantie avec els sfence, mfence, et je-sais-plus-quoi-fence.

Message édité le 27 septembre 2016 à 20:23:56 par godrik
lokilok lokilok
MP
Niveau 10
27 septembre 2016 à 22:32:54

Ah ok, je vois mieux comment ça fonctionne merci.

Pseudo supprimé
Niveau 6
27 septembre 2016 à 22:49:46

avec x86, on peut exécuter une instruction atomiquement en la préfixant avec LOCK.

     ADD [adresse], 123 ; pas atomique
LOCK ADD [adresse], 123 ; atomique

Les processeurs d'intel et d'amd on un cache global qui est partagé par tous les cœurs (L3 sur intel i7).
Chaque ligne d'adresse a un verrou dont la valeur est soit 0 (désactivé), soit le numéro du coeur détenteur (donc 3 bits pour pour un cpu 4 cœurs).

Maintenant il y a un truc qui me turlupine, godrik. si je prends par exemple l'instruction ADD-to-memory de ci dessus, je peux la décomposer en micro opérations suivantes


FETCH register, address    ; register := cache[address]
ADD   register, operand    ; register :+= operand
STORE address, register    ; cache[address] := register

maintenant, si j'ai

CORE #1                    CORE #2
  ADD [0xABCD], 123          LOCK ADD [0xABCD], 456

et que CORE #2 a un cycle de retard sur CORE #1


          CORE #1              CORE #2
	  
CYCLE 1   FETCH R0, 0xABCD     ...
CYCLE 2   ADD   R0, 123        LOCK   0xABCD
CYCLE 3   STALL (LOCKED)       FETCH  R0, 0xABCD
CYCLE 4   STALL (LOCKED)       ADD    R0, 456
CYCLE 5   STALL (LOCKED)       STORE  0xABCD, R0
CYCLE 6   STALL (LOCKED)       UNLOCK 0xABCD
CYCLE 7   STORE 0xABCD, R0     ...

il y a quand même un data race malgré l'utilisation de LOCK.

lokilok lokilok
MP
Niveau 10
27 septembre 2016 à 23:36:56

CodeArtisan, mais dans quel cas concret il serait utile de pouvoir faire des opérations atomique et non atomique sur un objet ? Avec les std::atomic par exemple tu ne peux que faire des opérations atomiques, donc ton cas de figure ne peut pas se présenter.

godrik godrik
MP
Niveau 22
28 septembre 2016 à 01:46:17

codeartisan, de memoire fetch_and_add est une seule instruction. Ou alors je n'ai pas compris ce que tu veux dire.

Message édité le 28 septembre 2016 à 01:47:07 par godrik
godrik godrik
MP
Niveau 22
28 septembre 2016 à 02:20:19

Ah, je viens de relire ce que tu disais. Tu parlais au niveau du microcode.

Tu as un core qui faire un fetch_and_add et l'autre qui fait un add non synchro. Donc en effet, tu vas avoir des une data race. Mais pourquoi faire ca ?
Souvent quand tu as un atomic et une autre operation non atomique, l'operation non atomique est souvent un read.

Pseudo supprimé
Niveau 6
28 septembre 2016 à 14:43:53

normalement l'effet d'une instruction atomique doit être vu par tous les processeurs or ici ce n'est pas le cas: core #0 ignore l'effet produit par core #1. J'ai regardé dans le manuel d'intel et j'ai trouvé

When one processor accesses data cached on another processor, it must not receive incorrect data. If it modifies data, all other processors that access that data must receive the modified data.

[...]

For the P6 family processors, locked operations serialize all outstanding load and store operations (that is, wait for them to complete). This rule is also true for the Pentium 4 and Intel Xeon processors, with one exception. Load operations that reference weakly ordered memory types (such as the WC memory type) may not be serialized.

En gros, chaque lock est précédé par un vidage (flush) des files d’attentes des processeurs. Je vais essayer d'avoir un preuve empirique.

godrik godrik
MP
Niveau 22
28 septembre 2016 à 15:53:55

Je ne vois pas en quoi core0 a ignore l'effet produit par core 1. Il a lu la donnee avant l'atomique. Et l'ecrit apres.

Pseudo supprimé
Niveau 6
28 septembre 2016 à 17:28:10

après avoir testé, je peux confirmer qu'il y a bien un data race si on mélange lock et non lock.

godrik godrik
MP
Niveau 22
28 septembre 2016 à 17:59:39

Evidement ;)

Pseudo supprimé
Niveau 6
28 septembre 2016 à 22:16:13

même si on a besoin que d'un bit, il vaut mieux avoir un verrou d'une taille de 64 octets (taille d'une ligne de cache)

avec struct { uint lock; uint data; }

 Performance counter stats for './a.out':

      11428.232641      task-clock:u (msec)  
                 0      context-switches:u                
                 0      cpu-migrations:u                   
                57      page-faults:u                 
    34,265,341,045      cycles:u    
     3,999,658,957      instructions:u    
     2,000,521,923      branches:u         
               343      branch-misses:u    

       9.339586720 seconds time elapsed

avec struct { uint lock; char pad[64]; uint data; }

 Performance counter stats for './a.out':

       9877.843435      task-clock:u (msec)            
                 0      context-switches:u                     
                 0      cpu-migrations:u                          
                59      page-faults:u                           
    29,607,404,289      cycles:u                  
     4,000,218,402      instructions:u  
     2,000,631,941      branches:u        
               301      branch-misses:u  

       7.887226292 seconds time elapsed

±même nombre d'instructions mais 15% plus rapide

Message édité le 28 septembre 2016 à 22:17:13 par
godrik godrik
MP
Niveau 22
29 septembre 2016 à 05:57:46

Et c'est quoi le code que tu testes ?

DébutPage précedente
12
Page suivanteFin
Répondre
Prévisu
?
Victime de harcèlement en ligne : comment réagir ?
Infos 0 connecté(s)

Gestion du forum

Modérateurs : godrik, LGV
Contacter les modérateurs - Règles du forum

Sujets à ne pas manquer

La vidéo du moment