Quelle est la différence entre une variable statique globale et une variable volatile statique?

StackOverflow https://stackoverflow.com/questions/346306

Question

J'ai utilisé une variable globale statique et une variable volatile statique dans la portée du fichier,

sont tous deux mis à jour par un ISR, une boucle principale et une boucle principale vérifient la valeur de la variable.

ici lors de l'optimisation, ni la variable globale ni la variable volatile ne sont optimisées. Ainsi, au lieu d’utiliser une variable volatile, une variable globale résout le problème.

Alors, est-il bon d'utiliser une variable globale au lieu de volatile?

Toute raison spécifique d'utiliser statique volatile ??

Tout programme d'exemple serait appréciable.

Merci d'avance ..

Était-ce utile?

La solution

Ce sont des choses différentes. Je ne suis pas un expert en sémantique volatile. Mais je pense que ce qui est décrit ici a du sens.

Global

Global signifie simplement que l'identifiant en question est déclaré à la portée du fichier. Il existe différents domaines d’application, appelés fonction (où les labels goto sont définis dans), le fichier (où résident les globaux), le bloc (où résident les variables locales normales) et le prototype de la fonction (où résident les paramètres de la fonction). Ce concept n'existe que pour structurer la visibilité des identifiants. Cela n'a rien à voir avec les optimisations.

Statique

static est une durée de stockage (nous ne le verrons pas ici) et un moyen de donner un nom déclaré dans le lien interne de la portée du fichier. Cela peut être fait pour des fonctions ou des objets requis uniquement dans une unité de traduction. Un exemple typique pourrait être une fonction help imprimant les paramètres acceptés et appelée uniquement à partir de la fonction main définie dans le même .c . fichier.

6.2.2 / 2 dans un brouillon C99:

  

Si la déclaration d'un & # 64257; scope   identifiant & # 64257; er pour un objet ou une fonction   contient la spécification de classe de stockage & # 64257; er   statique, l'identifiant a des fonctions internes   lien.

Liaison interne signifie que l'identificateur n'est pas visible en dehors de l'unité de traduction actuelle (comme la fonction help ci-dessus).

Volatile

La volatilité est une chose différente: ( 6.7.3 / 6 )

  

Un objet comportant une qualification volatile "# 64257".   le type peut être modifié de manière inconnue   la mise en œuvre ou avoir d'autres   effets secondaires inconnus. Donc tout   expression faisant référence à un tel objet   doit être évalué strictement selon   aux règles de la machine abstraite,   comme décrit au 5.1.2.3. En outre,   à chaque séquence point la dernière valeur   stocké dans l'objet doit être en accord avec   celle prescrite par l'abrégé   machine, sauf modification & # 64257; ed par le   facteurs inconnus mentionnés   précédemment.

La norme fournit un excellent exemple d'un exemple où volatile serait redondant ( 5.1.2.3/8 ):

  

Une implémentation peut entraîner une & # 64257   correspondance un à un entre   sémantique abstraite et actuelle: at   chaque point de séquence, les valeurs de   les objets réels seraient d'accord avec   ceux spécifiés par l'abrégé   sémantique. Le mot clé volatile   serait alors redondant.

Les

points de séquence sont des points où l’effet des effets secondaires concernant la machine abstraite est complet (c.-à-d. que les conditions externes telles que les valeurs de cellules mémoire ne sont pas incluses). Entre la droite et la gauche de & amp; & amp; et de || , après ; et après un appel de fonction, il y a des points de séquence par exemple.

La sémantique abstraite est ce que le compilateur peut déduire de ne voir que la séquence de code dans un programme particulier. Les effets des optimisations ne sont pas pertinents ici. La sémantique actuelle inclut l’effet des effets secondaires provoqués par l’écriture sur des objets (par exemple, le changement de cellules mémoire). Qualifier un objet de volatil signifie que l'on obtient toujours la valeur d'un objet directement à partir de la mémoire ("modifiée par les facteurs inconnus"). La norme ne mentionne les threads nulle part, et si vous devez vous fier à l'ordre des modifications ou à l'atomicité des opérations, vous devez utiliser des méthodes dépendantes de la plate-forme pour vous en assurer.

Pour une présentation facile à comprendre, intel propose un excellent article à ce sujet ici .

Que dois-je faire maintenant?

Continuez à déclarer vos données de fichier (globales) comme volatiles. Données globales i

Autres conseils

Tout d’abord, permettez-moi de mentionner qu’une variable globale statique est identique à une variable globale, sauf que vous limitez la variable à la portée du fichier. C'est à dire. vous ne pouvez pas utiliser cette variable globale dans d'autres fichiers via le mot clé extern .

Vous pouvez donc réduire votre question aux variables globales et variables volatiles.

À présent sur volatile:

Comme const , volatile est un modificateur de type.

Le mot clé volatile a été créé pour empêcher les optimisations du compilateur susceptibles de rendre le code incorrect, notamment en cas d'événements asynchrones.

Les objets déclarés comme volatile ne peuvent pas être utilisés dans certaines optimisations.

Le système lit toujours la valeur vraie actuelle d'un objet volatile au point où il est utilisé, même si une instruction précédente a demandé une valeur à partir du même objet. En outre, la valeur de l'objet est écrite immédiatement lors de l'affectation. Cela signifie qu'il n'y a pas de cache d'une variable volatile dans un registre de CPU.

Dr. Jobb a un excellent article sur les volatiles .

Voici un exemple tiré de l'article du Dr. Jobb:

class Gadget
{
public:
    void Wait()
    {
        while (!flag_)
        {
            Sleep(1000); // sleeps for 1000 milliseconds
        }
    }
    void Wakeup()
    {
        flag_ = true;
    }
    ...
private:
    bool flag_;
};

Si le compilateur voit que Sleep () est un appel externe, il supposera que Sleep () ne peut éventuellement pas modifier la valeur de la variable flag_. Le compilateur peut donc stocker la valeur de flag _ dans un registre. Et dans ce cas, cela ne changera jamais. Mais si un autre thread appelle le réveil, le premier thread lit toujours dans le registre du processeur. Wait () ne se réveillera jamais.

Alors, pourquoi ne pas simplement ne jamais mettre les variables en cache dans les registres et éviter complètement le problème? Il s'avère que cette optimisation peut réellement vous faire économiser beaucoup de temps. Donc C / C ++ vous permet de le désactiver explicitement avec le mot clé volatile .

Le fait ci-dessus que flag _ était une variable membre et non une variable globale (ni une variable statique) n'a pas d'importance. L'explication qui suit l'exemple donne le bon raisonnement, même s'il s'agit de variables globales (et de variables globales statiques).

Une idée fausse commune est que déclarer une variable volatile est suffisant pour assurer la sécurité des threads. Les opérations sur la variable ne sont toujours pas atomiques, même si elles ne sont pas "mises en cache". dans les registres

volatile avec des pointeurs:

Volatile avec les pointeurs, fonctionne comme constant avec les pointeurs.

Une variable de type volatile int * signifie que la variable pointée par le pointeur est volatile.

Une variable de type int * volatile signifie que le pointeur lui-même est volatil.

Le "volatil" mot-clé suggère au compilateur de ne pas effectuer certaines optimisations sur du code impliquant cette variable; si vous utilisez simplement une variable globale, rien n'empêche le compilateur d'optimiser à tort votre code.

Exemple:

#define MYPORT 0xDEADB33F

volatile char *portptr = (char*)MYPORT;
*portptr = 'A';
*portptr = 'B';

Sans "volatile", la première écriture peut être optimisée.

Le mot-clé volatile indique au compilateur de s’assurer que cette variable ne sera jamais mise en cache. Tous les accès doivent être faits de manière cohérente afin d'avoir une valeur cohérente entre tous les threads. Si la valeur de la variable doit être modifiée par un autre thread alors que vous avez une boucle pour vérifier le changement, vous voulez que la variable soit volatile, car rien ne garantit qu'une valeur de variable régulière ne sera pas mise en cache à un moment donné et que la boucle supposons simplement que cela reste le même.

Variable volatile sur Wikipedia

Ils ne sont peut-être pas différents dans votre environnement actuel, mais des modifications subtiles peuvent affecter le comportement.

  • Matériel différent (plus de processeurs, architecture de mémoire différente)
  • Une nouvelle version du compilateur avec une meilleure optimisation.
  • Variation aléatoire du temps entre les threads. Un problème ne peut se produire qu’une fois sur 10 millions.
  • Différents paramètres d'optimisation du compilateur.

À long terme, il est beaucoup plus prudent d'utiliser dès le début des concepts de multithreading appropriés, même si tout semble fonctionner pour le moment sans eux.

Bien sûr, si votre programme n'est pas multithread, peu importe.

Je +1 la réponse de friol. Je voudrais ajouter quelques précisions car il semble y avoir beaucoup de confusion dans les différentes réponses: la volatilité de C n'est pas volatile de Java.

Donc, d’abord, les compilateurs peuvent faire beaucoup d’optimisations en fonction du flux de données de votre programme, ce qui est volatile en C, cela vous permet de charger / stocker chaque fois à l’emplacement (au lieu d’utiliser des registres pour l’effacer). par exemple). C’est utile lorsque vous avez un port d’E / S mappé en mémoire, comme l’a souligné Friol.

Volatile in C n’a RIEN à voir avec les caches matériels ou le multithreading. Il n'insère pas de barrière de mémoire et vous ne pouvez absolument pas garantir l'ordre des opérations si deux threads y ont accès. Cependant, le mot clé volatile de Java fait exactement cela: insérer des barrières de mémoire si nécessaire.

variable volatile signifie que la valeur qui lui est affectée n’est pas constante, c’est-à-dire si une fonction contenant une variable volatile "a = 10" et la fonction ajoute 1 à chaque appel de cette fonction, elle retournera toujours la valeur mise à jour. { volatile int a = 10; a ++; } lorsque la fonction ci-dessus est appelée à plusieurs reprises, la variable a ne sera pas réinitialisée à 10, elle affichera toujours la valeur mise à jour jusqu'à l'exécution du programme. 1ère sortie = 10 alors 11 alors 12 et ainsi de suite.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top