Programmation parallèle§

Systèmes d’exploitation

auteur:Pierre-Antoine Champin
adresse:Département Informatique - IUT - Lyon1
licence:Ce travail est sous license Creative Commons Attribution-ShareAlike 3.0 France
1

Motivation§

2

Motivation§

Certaines applications peuvent avoir besoin d’effectuer plusieurs traitements en parallèle. Elles peuvent être constituées de plusieurs processus, bénéficiant ainsi:

Mais cette solution présente aussi des inconvénients :

3

Remarque§

4

Thread§

5

Définition§

6

Processus léger§

Les threads sont souvent appelés « processus légers ».

Ils bénéficient des mêmes avantages en terme de parallélisme, que les processus (l’ordonnanceur considère chaque thread individuellement).

Avantages par rapport aux processus :

7

Exemple§

void process_partial(section_t* sec) {
    // traite la section du tableau spécifiée par sec
    // ...
}

void process_all(int *tab) {
    pthread_t t;
    // traite la 1e moitié dans un thread séparé t
    pthread_create(&t, NULL, process_partial,
                   section(tab, 0, SIZE/2));
    // traite la 2e moitié dans ce thread
    process_partial(section(tab, SIZE/2, SIZE));
    // attend la fin du thread t
    pthread_join(t, NULL);
}
8

Green thread§

Dans certains contextes, les threads ne sont pas gérés par le noyau, mais émulés par un programme (par exemple la VM Java),

  • qui s’exécute en mode utilisateur (d’où l’appellation thread utilisateur, qu’on oppose aux threads noyaux) ;
  • et économise le temps de passer par le noyau (d’où l’appellation green thread).
9

Avantages et inconvénients§

Note

Concernant les appels systèmes bloquants, les bibliothèques et les VM qui implémentent les threads utilisateurs fournissent des fonctions à utiliser à leur place.

Ces fonctions sont pseudo-bloquantes, dans le sens ou elles « bloquent » le thread seul (pour leur ordonnanceur interne), mais utilisent en interne un appel non bloquant.

10

Coroutine§

11

Motivation§

La commutation préemptive entre les threads pose certains problèmes :

  • coûteuse en temps (surtout lorsqu’elle est gérée par le noyau),
  • pose de nombreux problèmes de synchronisation,
  • imposant des solutions de verouillages également pénalisant en temps.
12

Coroutine§

Des coroutines sont des fonctions qui ont vocation

Disponibles dans de nombreux langages :

13

Remarques§

14

Exemple générateur§

def chaine_de_traitement():
    for i in range(10):
        obj = produit(i)
        if obj.val == 0:
            continue
        elif obj.val == 2:
            consomme(obj[0])
            consomme(obj[1])
        else:
            consome(obj)
15

Exemple générateur§

def producteur():
    for i in range(10):
        yield produit(i)

def transformateur():
    for obj in producteur():
        if obj.val == 0:
            continue
        elif obj.val == 2:
            yield obj[0]
            yield obj[1]
        else:
            yield obj

def consommateur():
    for obj in transformateur():
        consomme(obj)
16

Exemple générateur§

def producteur():
    for i in range(10):
        yield produit(i)

def transformateur():
    for obj in producteur():
        if obj.val == 0:
            continue
        elif obj.val == 2:
            yield obj[0]
            yield obj[1]
        else:
            yield obj

def consommateur():
    for obj in transformateur():
        consomme(obj)
17

Exemple serveur§

def handle_request(req):
    rep1 = calcule_debut_reponse(req)
    req.send(rep1) # rend la main
    rep2 = calcule_fin_reponse(req, rep1)
    impacte_donnees_locales(rep1, rep2)
    req.send(rep2) # rend la main

Note

À l’avant-dernière ligne, on note qu’on modifie les données locales au serveur ; avec des threads, cette procédure comporterait des sections critiques, et devrait donc avoir recours à des mutex.

Avec des co-routines :

  • en mono-thread, il n’y a même pas besoin de mutex, puisque la préemption n’a lieu qu’aux moments des entrées / sorties, donc l’atomicité de la procédure est garantie ;
  • même si on utilise du multi-thread (sur une architecture multi-cœurs), on devra mettre des mutex, mais les situations ou ils seront bloquants seront beaucoup plus rares.
18

Conclusion§

19

En résumé§

20