lire la même structure sur tous les threads

Je veux que tous les threads lisent de la même structure. Je l’ai fait par le passé en ajoutant des threads dans la boucle qui lit la structure, mais cette fois, j’ai besoin que la structure soit ouverte dans void “dowork” comme le montre mon exemple.

J’ai le code suivant:

struct word_list { char word[20]; struct word_list * next; }; struct word_list * first_word = NULL; //other function which loads into struct is missing cause it's not relevant //in main() pthread_t thread_id[MAX_THREADS]; int max_thread = 10; for(t = 0 ; t file = file; args->t = t; args->e = ex; pthread_mutex_unlock(&thrd_list); if(pthread_create(&thread_id[t],NULL,dowork,args) != 0) { t--; fprintf(stderr,RED "\nError in creating thread\n" NONE); } } for(t = 0 ; t word,sizeof(myword) - 1); pthread_mutex_unlock(&thrd_list); //some irrelevant code is missing pthread_mutex_lock(&thrd_list); curr_word = curr_word->next; pthread_mutex_unlock(&thrd_list); } } 

Comment puis-je lire différents éléments de la même structure dans tous les threads?

Si je comprends maintenant vos exigences (et je pense que je le comprends enfin), vous devez traiter votre liste de mots comme une queue . Cela nécessite un mécanisme de notification permettant au “pousseur” d’éléments dans la queue d’informer les “extracteurs” que de nouvelles données sont disponibles. Un tel système existe déjà dans les pthreads: le mariage d’une variable de condition , d’un mutex , et du ou des prédicats qu’ils gèrent pour le contrôle du stream.

Ceci est un exemple d’utilisation. J’ai essayé de documenter ce qui se passe à chaque étape pour vous et j’espère que vous comprendrez.

 #include  #include  #include  #include  // defined the number of threads in our queue and the number // of test items for this demonstration. #define MAX_THREADS 16 #define MAX_ITEMS 128*1024 typedef struct word_list { char word[20]; struct word_list * next; } word_list; // predicate values for the word list struct word_list * first_word = NULL; // current word. int word_shutdown = 0; // shutdown state // used for protecting our list. pthread_mutex_t wq_mtx = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t wq_cv = PTHREAD_COND_INITIALIZER; // worker proc void *dowork(void*); int main() { pthread_t thread_id[MAX_THREADS]; int i=0; // start thread pool for(i=0; i < MAX_THREADS; ++i) pthread_create(thread_id+i, NULL, dowork, NULL); // add MAX_ITEMS more entries, we need to latch since the // work threads are actively processing the queue as we go. for (i=0;iword, "Word-%d", i); // latch before updating the queue head. pthread_mutex_lock(&wq_mtx); node->next = first_word; first_word = node; // no longer need the latch. unlock and inform any // potential waiter. pthread_mutex_unlock(&wq_mtx); pthread_cond_signal(&wq_cv); } // wait for the condition that the queue is empty pthread_mutex_lock(&wq_mtx); while (first_word != NULL) pthread_cond_wait(&wq_cv, &wq_mtx); pthread_mutex_unlock(&wq_mtx); // queue is empty, but threads are all still there waiting. So // do it again, just to proves the pool is still intact. for (i=0;iword, "Word-%d", i); // latch before updating the queue head. pthread_mutex_lock(&wq_mtx); node->next = first_word; first_word = node; // no longer need the latch. unlock and inform any // potential waiter. pthread_mutex_unlock(&wq_mtx); pthread_cond_signal(&wq_cv); } // again wait for the condition that the queue is empty pthread_mutex_lock(&wq_mtx); while (first_word != NULL) pthread_cond_wait(&wq_cv, &wq_mtx); // queue is empty, and we're not adding anything else. latch // the mutex, set the shutdown flag, and tell all the threads. // they need to terminate. word_shutdown = 1; pthread_mutex_unlock(&wq_mtx); pthread_cond_broadcast(&wq_cv); for (i=0;inext; if (p->next) pthread_cond_signal(&wq_cv); pthread_mutex_unlock(&wq_mtx); // // TODO: process item here. // ++n_processed; free(p); } else if (word_shutdown != 0) break; } // we still own the mutex. report on how many items we received, then // one more signal to let someone (anyone, actually) know we're done. pthread_t self = pthread_self(); printf("%p : processed %d items.\n",self, n_processed); pthread_mutex_unlock(&wq_mtx); pthread_cond_signal(&wq_cv); return NULL; } 

Exemple de sortie: MAX_THREADS = 4 (votre sortie variera)

 0x100387000 : processed 64909 items. 0x100304000 : processed 64966 items. 0x1000b5000 : processed 64275 items. 0x100281000 : processed 67994 items. 

Exemple de sortie: MAX_THREADS = 8

 0x100304000 : processed 31595 items. 0x1000b5000 : processed 33663 items. 0x100593000 : processed 34298 items. 0x10040a000 : processed 32304 items. 0x10048d000 : processed 32406 items. 0x100387000 : processed 31878 items. 0x100281000 : processed 32317 items. 0x100510000 : processed 33683 items. 

Exemple de sortie: MAX_THREADS = 16

 0x10079f000 : processed 17239 items. 0x101081000 : processed 16530 items. 0x101104000 : processed 16662 items. 0x100699000 : processed 16562 items. 0x10040a000 : processed 16672 items. 0x100593000 : processed 15158 items. 0x10120a000 : processed 17365 items. 0x101187000 : processed 14184 items. 0x100387000 : processed 16332 items. 0x100616000 : processed 16497 items. 0x100281000 : processed 16632 items. 0x100304000 : processed 16222 items. 0x100510000 : processed 17188 items. 0x10048d000 : processed 15367 items. 0x1000b5000 : processed 16912 items. 0x10071c000 : processed 16622 items. 

Et juste parce que nous pouvons, avec l’optimisation globale complète activée

Exemple de sortie: MAX_THREADS = 32, MAX_ITEMS = 4194304

 0x109c58000 : processed 260000 items. 0x109634000 : processed 263433 items. 0x10973a000 : processed 262125 items. 0x10921c000 : processed 261201 items. 0x108d81000 : processed 262325 items. 0x109a4c000 : processed 262318 items. 0x108f8d000 : processed 263107 items. 0x109010000 : processed 261382 items. 0x109946000 : processed 262299 items. 0x109199000 : processed 261930 items. 0x10929f000 : processed 263506 items. 0x109093000 : processed 262362 items. 0x108e87000 : processed 262069 items. 0x108e04000 : processed 261890 items. 0x109acf000 : processed 261875 items. 0x1097bd000 : processed 262040 items. 0x109840000 : processed 261686 items. 0x1093a5000 : processed 262547 items. 0x109b52000 : processed 261980 items. 0x109428000 : processed 264259 items. 0x108f0a000 : processed 261620 items. 0x1095b1000 : processed 263062 items. 0x1094ab000 : processed 261811 items. 0x1099c9000 : processed 262709 items. 0x109116000 : processed 261628 items. 0x109bd5000 : processed 260905 items. 0x10952e000 : processed 262741 items. 0x1098c3000 : processed 260608 items. 0x109322000 : processed 261970 items. 0x1000b8000 : processed 262061 items. 0x100781000 : processed 262669 items. 0x1096b7000 : processed 262490 items. 

Hmmm. et je n’ai pas utilisé volatile dans tout cela. Doit être le temps d’acheter un billet de loto.

Quoi qu’il en soit, je suggère de faire des recherches sur les pthreads, en particulier sur le contrôle des mutex et des variables de condition et leurs interactions. J’espère que cela vous aide.

Vous souhaitez donc traiter beaucoup de données en répartissant le travail sur plusieurs threads. Votre solution n’est pas très efficace, car vos threads vont se battre contre le propriétaire du mutex et vous ne pouvez pas être sûr que le travail est réparti uniformément sur tous vos threads. Ainsi, par exemple, les threads 0 et 1 pourraient obtenir tout le travail car ils ont d’abord access au mutex et tous les autres threads sont simplement inactifs tout le temps.

Si vous souhaitez améliorer les performances, vous devez procéder comme suit: –

  • Rendre tous les threads indépendants les uns des autres, c’est-à-dire supprimer les données synchronisées
  • Assurez la cohérence de la mémoire entre les threads, c’est-à-dire que les données de l’élément n + 1 sont proches de celles de l’élément n. Cela aide la CPU à mieux accéder à la mémoire. En contournant beaucoup la RAM, vous obtiendrez de nombreuses erreurs de cache qui réduisent les performances.

Ainsi, dans votre programme, au lieu d’une liste chaînée unique partagée par tous les threads, créez une liste chaînée pour chaque thread: –

 typedef struct _word_list { //data struct _word_list *next; } word_list; static const int num_threads = 4; // actually, setting this to number of CPUs at run time would be better word_list *lists [num_threads] = {0}; void ReadWords () { word_list **current [num_threads]; for (int i = 0 ; i < num_threads ; ++i) { current = &lists [i]; } int destination = 0; while (read some valid input) { *current [destination] = malloc (sizeof (word_list)); // set data current [destination] = &current [destination]->next; destination = (destination + 1) % num_threads; } // data has now been read and stored into a series of linked lists, each list having // the same number of items (or one less) } void create_threads () { for (int i = 0 ; i < num_threads ; ++i) { // create thread, and pass it the value of lists [i] } } void do_work (...) { for (word_list *item = passed in parameter ; item ; item = item->next) { process data } } 

Dans ce programme (je viens de le créer, je ne l’ai pas vérifié), je crée quatre listes chaînées et atsortingbue les données de manière égale aux listes. Ensuite, je crée les discussions et donne à chaque discussion une des listes liées. Chaque thread traite ensuite sa propre liste chaînée (ce sont des listes séparées).

Chaque thread peut maintenant fonctionner à pleine vitesse et ne jamais avoir à attendre un mutex pour obtenir des données. Les access en mémoire sont raisonnables mais dépendent dans une large mesure de l’allocateur. L’utilisation d’un tableau plutôt que d’une liste chaînée améliorerait cela, mais vous auriez besoin de connaître le nombre d’éléments de données avant d’allouer les tableaux, ce qui pourrait ne pas être possible.

Laisse-moi voir si je comprends bien?

  • struct word_list décrit une sorte de liste chaînée
  • Vous souhaitez étaler les éléments de cette liste parmi les threads.

Si c’est ce que vous voulez, je voudrais simplement extraire les éléments un par un de la liste et écrire le pointeur vers le rest:

 volatile struct word_list * first_word = NULL; // important to make it volatile void *dowork(void *arguments) { struct word_list * curr_word; char myword[20]; do { // gain exclusive access to the control structures pthread_mutex_lock(&thrd_list); // get the next element curr_word = first_word; if (curr_word == NULL) { pthread_mutex_unlock(&thrd_list); break; } // notify the remaining threads what the next element is first_word = curr_word->next; pthread_mutex_unlock(&thrd_list); // do whatever you have to do } while (1); } 

Créez une liste de volatile struct word_list * next_word globale volatile struct word_list * next_word si vous ne souhaitez pas modifier first_word . Assurez-vous de le rendre volatile , sinon le compilateur peut effectuer des optimisations menant à des résultats étranges.