Comment" multithread " code C

J'ai une application de crunching numérique écrite en C. c'est une sorte de boucle principale qui, pour chaque valeur, appelle, pour augmenter les valeurs de "i", une fonction qui effectue des calculs. J'ai lu à propos du multithreading, et j'envisage d'apprendre un peu à ce sujet, en C. je me demande si un code général comme le mien pourrait être automatiquement multithread et comment.

Merci

P. D. pour avoir une idée de mon code, disons que c'est quelque chose comme ceci:

main(...)
for(i=0;i<=ntimes;i++)get_result(x[i],y[i],result[i]);

...

void get_result(float x,float y,float result){
  result=sqrt(log (x) + log (y) + cos (exp (x + y));
(and some more similar mathematical operations)
}
31
demandé sur flow 2010-10-11 20:19:13

12 réponses

Une alternative à multithread votre code serait d'utiliser pthreads (fournit un contrôle plus précis Qu'OpenMP).

En supposant x, y & result sont des tableaux de variables globales,

#include <pthread.h>

...

void *get_result(void *param)  // param is a dummy pointer
{
...
}

int main()
{
...
pthread_t *tid = malloc( ntimes * sizeof(pthread_t) );

for( i=0; i<ntimes; i++ ) 
    pthread_create( &tid[i], NULL, get_result, NULL );

... // do some tasks unrelated to result    

for( i=0; i<ntimes; i++ ) 
    pthread_join( tid[i], NULL );
...
}

(compilez votre code avec gcc prog.c -lpthread)

17
répondu 2010-10-16 17:25:31

Si la tâche est hautement parallélisable et que votre compilateur est moderne, vous pouvez essayer OpenMP. http://en.wikipedia.org/wiki/OpenMP

26
répondu Novikov 2010-10-11 16:21:56

Si vous espérez fournir une concurrence pour une seule boucle pour une sorte de calcul scientifique ou similaire, OpenMP comme @Novikov dit est vraiment votre meilleur pari; c'est pour cela qu'il a été conçu.

Si vous cherchez à apprendre l'approche plus classique que vous verriez plus généralement dans une application écrite en C... Sur POSIX, vous voulez pthread_create() et al. Je ne suis pas sûr de ce que votre arrière-plan pourrait être avec la concurrence dans d'autres langues, mais avant d'aller trop profondément dans cela, vous voudrez pour connaître vos primitives de synchronisation (mutex, sémaphores, etc.) assez bien, ainsi que la compréhension lorsque vous aurez besoin de les utiliser. Ce sujet pourrait être un livre entier ou un ensemble de questions SO à lui-même.

9
répondu asveikau 2010-10-11 17:31:30

Vous devriez jeter un oeil à openMP pour cela. L'exemple C / C++ sur cette page est similaire à votre code: https://computing.llnl.gov/tutorials/openMP/#SECTIONS

#include <omp.h>
#define N     1000

main ()
{

int i;
float a[N], b[N], c[N], d[N];

/* Some initializations */
for (i=0; i < N; i++) {
  a[i] = i * 1.5;
  b[i] = i + 22.35;
  }

#pragma omp parallel shared(a,b,c,d) private(i)
  {

  #pragma omp sections nowait
    {

    #pragma omp section
    for (i=0; i < N; i++)
      c[i] = a[i] + b[i];

    #pragma omp section
    for (i=0; i < N; i++)
      d[i] = a[i] * b[i];

    }  /* end of sections */

  }  /* end of parallel section */

}

Si vous préférez ne pas utiliser openMP, vous pouvez utiliser pthreads ou clone/wait directement.

Peu importe l'itinéraire que vous choisissez, vous divisez simplement vos tableaux en morceaux que chaque thread traitera. Si tout votre traitement est purement informatique (comme suggéré par votre exemple de fonction), alors vous devriez faire bien d'avoir seulement autant de threads que vous avez de processeurs logiques.

Il y a des frais généraux avec l'ajout de threads pour faire un traitement parallèle, alors assurez-vous de donner à chaque thread suffisamment de travail pour le compenser. Habituellement, vous le ferez, mais si chaque thread ne finit qu'avec 1 Calcul à faire et que les calculs ne sont pas si difficiles à faire, vous pouvez réellement ralentir les choses. Vous pouvez toujours avoir moins de threads que de processeurs si c'est le cas.

Si vous le faites avoir des E / S dans votre travail, alors vous pouvez constater qu'avoir plus de threads que de processeurs est une victoire car alors qu'un thread peut bloquer en attendant que certains E / S complètent un autre thread peut faire ses calculs. Vous devez faire attention à faire IO dans le même fichier dans les threads, cependant.

8
répondu nategoose 2010-10-14 15:47:02

Selon le système D'exploitation, vous pouvez utiliser des threads posix. Vous pouvez plutôt implémenter le multithreading sans pile en utilisant des machines d'état. Il y a un très bon livre intitulé "embedded multitâche" par Keith E. Curtis. C'est juste un ensemble soigneusement conçu d'instructions de cas de commutateur. Fonctionne très bien, je l'ai utilisé sur tout d'apple Mac, Rabbit semiconductor, AVR, PC.

Vali

3
répondu ValiRossi 2010-10-11 17:07:14

Le compilateur C++ D'Intel est en fait capable de paralyser automatiquement votre code. C'est juste un commutateur de compilateur que vous devez activer. Cela ne fonctionne pas aussi bien que OpenMP (ie. il ne réussit pas toujours ou le programme résultant est plus lent). Depuis le site web d'Intel: "L'auto-parallélisation, qui est déclenchée par l'option-parallel (Linux* OS et Mac OS* X) ou /Qparallel (Windows* OS), identifie automatiquement les structures de boucle qui contiennent le parallélisme. Pendant la compilation, le compilateur tente automatiquement de déconstruire les séquences de code en threads séparés pour un traitement parallèle. Aucun autre effort du programmeur n'est nécessaire."

3
répondu darklon 2010-10-18 17:21:37

Un bon exercice pour apprendre la programmation simultanée dans n'importe quelle langue serait de travailler sur une implémentation de pool de threads.
Dans ce modèle, vous créez des threads à l'avance. Ces threads sont traités comme une ressource. Un objet/structure de pool de threads est utilisé pour attribuer une tâche définie par l'utilisateur à ces threads pour l'exécution. Lorsque la tâche est terminée, vous pouvez collecter ses résultats. Vous pouvez utiliser le pool de threads comme modèle de conception à usage général pour la concurrence. L'idée principale pourrait ressembler à

#define number_of_threads_to_be_created 42
// create some user defined tasks
Tasks_list_t* task_list_elem = CreateTasks();
// Create the thread pool with 42 tasks
Thpool_handle_t* pool = Create_pool(number_of_threads_to_be_created);

// populate the thread pool with tasks
for ( ; task_list_elem; task_list_elem = task_list_elem->next) {
   add_a_task_to_thpool (task_list_elem, pool);
}
// kick start the thread pool
thpool_run (pool);

// Now decide on the mechanism for collecting the results from tasks list.
// Some of the candidates are:
// 1. sleep till all is done (naive)
// 2. pool the tasks in the list for some state variable describing that the task has
//    finished. This can work quite well in some situations
// 3. Implement signal/callback mechanism that a task can use to signal that it has 
//    finished executing.

Le mécanisme de collecte des données des tâches et la quantité de threads utilisés dans le pool doivent être choisis pour refléter vos besoins et les capacités de l'environnement matériel et d'exécution.
Notez également que ce modèle ne dit rien comment vous devez "synchroniser" vos tâches les unes avec les autres / à l'extérieur. La gestion des erreurs peut également être un peu délicate (exemple: que faire lorsqu'une tâche échoue). Ces deux aspects doivent être pensés à l'avance - ils peuvent restreindre l'utilisation du modèle de pool de threads.

Sur fil pool:
http://en.wikipedia.org/wiki/Thread_pool_pattern
http://docs.oracle.com/cd/E19253-01/816-5137/ggedn/index.html

Une bonne littérature sur pthreads pour y aller:
http://www.advancedlinuxprogramming.com/alp-folder/alp-ch04-threads.pdf

3
répondu Marcin 2014-06-08 11:56:36

Pour aborder spécifiquement la partie "automatiquement multithread " de la question de L'OP:

Une vue vraiment intéressante de la façon de programmer le parallélisme a été conçue dans un langage appelé Cilk plus inventé par le MIT et maintenant détenu par Intel. Pour citer Wikipedia, l'idée est que

" le programmeur devrait être responsable pour exposer le parallélisme, identifier les éléments qui peuvent en toute sécurité être exécuté en parallèle; il devrait puis être laissé à la au moment de l'exécution de l'environnement, en particulier la planificateur, pour décider pendant l'exécution comment répartir le travail entre les processeurs."

Cilk Plus est un sur-Ensemble de C++standard. Il contient juste quelques mots clés supplémentaires(_Cilk_spawn, _Cilk_sync, et _Cilk_for) qui permettent au programmeur de marquer des parties de leur programme comme parallélisable. Le programmeur ne mandate que n'importe quel code soit exécuté sur un nouveau thread, il autorise simplement le planificateur d'exécution léger à générer un nouveau thread si et seulement si elle est effectivement la bonne chose à faire dans les conditions d'exécution.

Pour utiliser Cilk Plus, ajoutez simplement ses mots-clés dans votre code, et construisez avec le compilateur C++ D'Intel .

2
répondu AlcubierreDrive 2010-10-17 05:26:22

Votre code n'est pas automatiquement multithread par le compilateur si c'était votre question. Veuillez noter que les normes C elles-mêmes ne savent rien sur le multi-threading, car si vous pouvez utiliser le multi-threading ou non ne dépend pas de la langue que vous utilisez pour le codage, mais de la plate-forme de destination pour laquelle vous codez. Le Code écrit en C peut fonctionner sur à peu près n'importe quoi pour lequel un compilateur C existe. Un compilateur C existe même pour un ordinateur C64 (presque complètement conforme à ISO-99); cependant, pour prendre en charge plusieurs threads, la plate-forme doit avoir un système d'exploitation prenant en charge cela et cela signifie généralement qu'au moins certaines fonctionnalités du processeur doivent être présentes. Un système d'exploitation peut faire le multithreading presque exclusivement dans le logiciel, ce sera terriblement lent et il n'y aura pas de protection de la mémoire, mais c'est possible, mais même dans ce cas, vous avez besoin d'au moins des interruptions programmables.

Alors, comment écrire du code C multi-thread dépend entièrement du système d'exploitation de votre plate-forme cible. Il existe des systèmes conformes à POSIX (OS X, FreeBSD, Linux, etc.) et les systèmes qui ont leur propre bibliothèque pour cela (Windows). Certains systèmes ont plus de bibliothèque pour cela (par exemple OS X a la bibliothèque POSIX, mais il y a aussi le Gestionnaire de threads de carbone que vous pouvez utiliser en C (bien que je pense que c'est plutôt un héritage de nos jours).

Bien sûr, il existe des bibliothèques de threads multiplateformes et certains compilateurs modernes prennent en charge des choses comme OpenMP, où le compilateur construire du code pour créer des threads sur votre plate-forme cible choisie; mais peu de compilateurs le supportent et ceux qui le supportent ne sont généralement pas complets. Habituellement, vous obtenez le support système le plus large en utilisant des threads POSIX, plus souvent appelés "pthreads". La seule plate-forme majeure ne le supportant pas est Windows et ici vous pouvez utiliser des bibliothèques tierces gratuites comme celle-ci. Plusieurs autres ports existent également (Cygwin en a un à coup sûr). Si vous aurez une interface utilisateur un jour de quelque sorte, vous pouvez utiliser une bibliothèque multi-plateforme comme wxWidgets ou SDL, les deux offrant un support multi-thread cohérent sur toutes les plates-formes prises en charge.

1
répondu Mecki 2010-10-18 14:04:30

Si une itération en boucle est indépendante de celles qui la précèdent, alors il y a une approche très simple: essayez le multi-traitement, plutôt que le multi-threading.

Disons que vous avez 2 cœurs et ntimes est 100, puis 100/2=50, alors créez 2 versions du programme où la première itère de 0 à 49, l'autre de 50 à 99. Exécutez-les tous les deux, vos cœurs devraient être très occupés.

C'est une approche très simpliste, mais vous n'avez pas à jouer avec la création de threads, la synchronisation, etc

1
répondu ifyes 2010-10-18 15:32:50

Vous pouvez utiliser pthreads pour effectuer le multithreading dans C. voici un exemple simple basé sur pthreads.

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

void *mythread1();  //thread prototype
void *mythread2();

int main(){
    pthread_t thread[2];
    //starting the thread
    pthread_create(&thread[0],NULL,mythread1,NULL);
    pthread_create(&thread[1],NULL,mythread2,NULL);
    //waiting for completion
    pthread_join(thread[0],NULL);
    pthread_join(thread[1],NULL);


    return 0;
}

//thread definition
void *mythread1(){
    int i;
    for(i=0;i<5;i++)
        printf("Thread 1 Running\n");
}
void *mythread2(){
    int i;
    for(i=0;i<5;i++)
        printf("Thread 2 Running\n");
}

Référence: Programme C pour implémenter Multithreading-Multithreading en C

1
répondu Mohd Shibli 2017-10-07 03:30:33

C11 threads dans glibc 2.28.

Testé dans Ubuntu 18.04 (glibc 2.27) en compilant glibc à partir de la source: plusieurs bibliothèques glibc sur un seul hôte

Exemple de: https://en.cppreference.com/w/c/language/atomic

#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>

atomic_int acnt;
int cnt;

int f(void* thr_data)
{
    for(int n = 0; n < 1000; ++n) {
        ++cnt;
        ++acnt;
        // for this example, relaxed memory order is sufficient, e.g.
        // atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed);
    }
    return 0;
}

int main(void)
{
    thrd_t thr[10];
    for(int n = 0; n < 10; ++n)
        thrd_create(&thr[n], f, NULL);
    for(int n = 0; n < 10; ++n)
        thrd_join(thr[n], NULL);

    printf("The atomic counter is %u\n", acnt);
    printf("The non-atomic counter is %u\n", cnt);
}

Compiler et exécuter:

gcc -std=c11 main.c -pthread
./a.out

Sortie Possibles:

The atomic counter is 10000
The non-atomic counter is 8644

Les threads POSIX

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <stdlib.h>
#include <pthread.h>

enum CONSTANTS {
    NUM_THREADS = 1000,
    NUM_ITERS = 1000
};

int global = 0;
int fail = 0;
pthread_mutex_t main_thread_mutex = PTHREAD_MUTEX_INITIALIZER;

void* main_thread(void *arg) {
    int i;
    for (i = 0; i < NUM_ITERS; ++i) {
        if (!fail)
            pthread_mutex_lock(&main_thread_mutex);
        global++;
        if (!fail)
            pthread_mutex_unlock(&main_thread_mutex);
    }
    return NULL;
}

int main(int argc, char **argv) {
    pthread_t threads[NUM_THREADS];
    int i;
    fail = argc > 1;
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_create(&threads[i], NULL, main_thread, NULL);
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_join(threads[i], NULL);
    assert(global == NUM_THREADS * NUM_ITERS);
    return EXIT_SUCCESS;
}

Compiler et exécuter:

gcc -std=c99 pthread_mutex.c -pthread
./a.out
./a.out 1

La première exécution fonctionne bien, la seconde échoue en raison de l'absence synchronisation.

Testé sur Ubuntu 18.04. GitHub amont .

0