Attente active

Un article de Wikipédia, l'encyclopédie libre.

En génie logiciel, l'attente active ou "polling" est une technique de programmation que les processus utilisent lorsqu'ils vérifient de façon répétées si une condition est vraie, comme par exemple l'attente d'une entrée (clavier ou autre) ou encore la libération d'un verrou.

Cette technique peut également être utilisée pour mettre en attente un programme pour une durée déterminée. Cela était nécessaire sur d'anciens systèmes d'exploitations dont le matériel sous-jacent ne proposaient pas de méthode spécifique pour suspendre l'exécution du flot d'instruction pendant une période déterminée. Sur les plateformes modernes proposant des horloges et plusieurs vitesses de processeur, cette forme de suspension du programme est souvent imprécise et un signe de programmation naïve.

L'attente active peut, par contre, être une stratégie valide dans certaines circonstances, le plus souvent dans l'implémentation des spinlocks au sein de systèmes d'exploitation conçus pour fonctionner sur des systèmes à processeurs multiples. À part ce genre de cas, les attentes actives devraient être évitées, puisque le CPU pourrait être réattribué à une autre tâche.

Sommaire

[modifier] Exemple de code en C

Le code en C plus bas montre deux threads qui partagent un entier global i. Le premier thread utilise une attente active pour surveiller un changement de la valeur de i.

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
 
volatile int i; /* i est global, elle est donc visible à toute les
                   fonctions. il est également marqué volatile, car
                   sa valeur sera modifiée d'une façon qui n'est pas
                   prédictible par le compilateur (dans notre cas, 
                   ce sera l'autre thread qui modifiera la valeur). */
 
/* t1 utilise une attente active pour surveiller la modification 
   de la valeur de i. */
static void *f1()
{
    while (i==0)
    {
        /* On ne fait rien, à par vérifier la valeur de i */
    } 
    printf("La valeur de i a changée pour %d.\n", i);
 
    return;
}
 
static void *f2()
{
    sleep(60); /* Attente de 60 secondes. */
    i = 99;
    printf("t2 change la valeur de i à %d.\n", i);
 
    return;
}
 
int main()
{
    int x;
    pthread_t t1, t2;
    i = 0; /* On fixe la variable globale i à 0. */
 
    x = pthread_create(&t1, NULL, f1, NULL);
    if (x != 0)
    {
        printf("erreur lors de la création de t1.\n");
    }
 
    x = pthread_create(&t2, NULL, f2, NULL);
    if (x != 0)
    {
        printf("erreur lors de la création de t2.\n");
    }
 
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("Tous les threads ont terminés.\n");
    return 0;
}

Sur un système de type Unix, vous pouvez compiler le code ci-dessus de la façon suivante :

$ cc spinlock.c -lpthread

[modifier] Utilisation du processeur

Dans l'exemple ci-dessus, le second thread (t2) est endormi immédiatement pour 60 secondes. Pendant ce temps, le premier thread vérifie constamment si le second thread a modifié la valeur de i.

Vous pouvez utiliser les outils top ou uptime communément installé sur les systèmes de type Unix pour voir comment ce programme utilise le processeur. Lancez le programme comme indiqué :

$ uptime; ./a.out ; uptime
13:25:47 up 53 days, 23:50,  4 users,  load average: 0.00, 0.00, 0.00
t2 change la valeur de i à 99.
La valeur de i a changée pour 99.
Tous les threads ont terminés.
13:26:47 up 53 days, 23:51,  4 users,  load average: 0.75, 0.21, 0.07

Bien sur, chaque système retournera des nombres plus ou moins différents, mais l'important est de remarquer qu'avant de lancer le programme, la charge moyenne du système sur la minute précédente était de 0.00. À la fin de l'exécution de ce programme, la charge moyenne de la dernière minute est montée à 0.75, ce qui est surprenant vu la faible complexité du programme.

[modifier] Des alternatives à l'attente active

Une alternative est d'utiliser des signaux. La plupart des systèmes d'exploitation et autre bibliothèques de thread permettent d'endormir le premier thread, pour que celui-ci soit réveillé plus tard par un signal envoyé par le second thread lors du changement de la valeur de i. Cette méthode, connue comme programmation sur événement est plus efficace car le thread ne consomme pas de cycle CPU lorsqu'il est endormi.

L'attente active en elle même peut être grandement améliorée en utilisant une fonction de pause (sleep) trouvable sur la plupart des systèmes d'exploitation. Ainsi le thread sera mis en pause de façon régulière avec un temps d'attente relativement court (une seconde voir moins) pendant lequel il ne gaspillera pas de temps CPU. Si la boucle se contente de tester quelque chose de simple, alors le thread passera la plupart de son temps endormi, et ne gaspillera pas une grande proportion du temps CPU, même si quelques cycles seront toujours consommés.

[modifier] Cas où l'attente active est appropriée

En programmation bas niveau, dans le cas des pilotes matériels, quelques fois les attentes actives sont nécessaires. Il n'est en effet pas toujours pratique d'inclure un système de signal par interruption pour tous les dispositifs matériels, et en particulier pour ceux qui sont rarement utilisé. Parfois, il est nécessaire d'écrire des informations de contrôle dans un registre du matériel en question, et d'y lire ensuite l'état de ce matériel provoqué par l'écriture précédente. Ces dernières données ne sont parfois pas valides avant quelques cycles, ou dizaines de cycles d'horloges. Le programmeur pourrait utiliser une fonction fournie par le système, mais l'appel à cette fonction prendrait plus de cycle CPU que le temps que le matériel mettrait pour valider les données (en ne considérant pas le cas du changement de thread).

Dans ce genre de cas, il est habituel d'utiliser une attente active qui lirait l'état du périphérique jusqu'à ce que celui-ci soit valide. Appeler une fonction du système, quelle qu'elle soit, serait dans ce cas bien moins efficace qu'une attente active de quelques cycles.

[modifier] Voir aussi

[modifier] Liens externes (en anglais)