Dans un programme multithread (Java ou .Net), puis-je supposer que la copie d'une variable est atomique?

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

Question

Je m'inquiétais des conditions de course dans une application que je conçois, alors que je me posais des questions sur cette question.

Supposons que j'ai un grand tableau ou une collection quelconque gérée par un composant de mon programme, appelons ce composant Monitor. Son travail consiste à vérifier régulièrement si la collection est "sale", i. e. a récemment changé et, dans l’affirmative, écrivez un instantané sur le disque (il s’agit de vérifier l’application en cas de blocage) et de le marquer comme propre à nouveau.

D'autres composants du même programme, s'exécutant dans un autre thread, appellent les méthodes du moniteur pour ajouter ou modifier des données dans le tableau / la collection. Ces méthodes marquent la collection comme étant sale.

Maintenant, les méthodes de modification s'exécutent dans les threads des autres composants, n'est-ce pas? Et si je ne suis pas aussi chanceux, ils pourraient être appelés pendant que l'instantané est écrit sur le disque, modifier les données déjà écrites, définir le drapeau modifié et le thread du moniteur le désactive juste après cela, sans avoir enregistré les modifications (il était déjà passé l’élément quand il a changé). J'ai donc une collection sale marquée comme étant propre.

Pendant un moment, j’ai pensé que je pourrais peut-être résoudre le problème en créant une copie temporaire de la collection, en la marquant comme propre, puis en la numérisant. Mais la copie serait-elle atomique, i. e. puis-je être sûr que la collection ne changera pas pendant que je la copie?

Entre temps, je pense avoir trouvé de meilleures solutions, comme

  • définir un indicateur de verrouillage avant de commencer l'écriture sur le disque et faire attendre les méthodes de modification des données jusqu'à ce que l'indicateur soit désactivé
  • demander aux méthodes de modification de données d'écrire dans une "file d'attente de modification" au lieu de passer directement à la collection et d’avoir le thread qui effectue le processus d’écriture sur le disque qui file

et je pense que le drapeau de verrouillage pourrait être la meilleure voie à suivre. Mais je suis toujours curieux: la copie est-elle une variable atomique?

Suivi : Cela devrait peut-être aller de soi, mais en réalité c'est à peu près la même chose. Selon les réponses ci-dessous, mon "indicateur de verrouillage". approche pourrait également ne pas fonctionner, non? Parce que la méthode de modification des données peut vérifier l’indicateur de verrouillage lorsqu’il est défini sur le paramètre " verrouillé " valeur et décider que ce n'est pas verrouillé. Donc, j'ai besoin d'une construction spéciale comme un mutex si je veux vraiment le faire correctement, correct?

Félicitations à erickson pour son réponse très utile sur mon suivi. J'aurais vraiment dû poser ces deux questions pour pouvoir accepter deux réponses. S'il vous plaît, votez lui aussi.

Était-ce utile?

La solution

Non. Par exemple, les variables longues en Java ne sont pas atomiques sur les machines 32 bits.

De plus, il existe un " mise en cache de threads " issue - sauf si votre variable est volatile ou dans un bloc synchronisé, un autre thread peut ne pas voir le changement de valeur de variable. Ceci est vrai pour tous les types de variables, pas seulement les longues.

Lisez ici: http://gee.cs.oswego.edu/dl /cpj/jmm.html , en particulier "atomicity" et " visibilité " paragraphes.

Autres conseils

Non, ce n'est pas atomique. Consultez cette question pour savoir pourquoi et quoi faire à ce sujet.

Découvrez java.util.concurrent.atomic - vous pouvez utiliser certains éléments intéressants.

Vous devez vous préoccuper de la visibilité des modifications apportées à d'autres threads lorsque vous travaillez sur la machine virtuelle Java. En général, vous devez effectuer vos assignations dans un bloc synchronisé , ou bien la variable doit être volatile ou utilisez un wrapper de variable du java.util. package.atomic concurrent .

Cependant, dans votre cas, il semble que vous n’ayez qu’un seul fil qui efface le champ "sale". indicateur le fil qui persiste dans les données. Si tel est le cas, désactivez l'indicateur avant d'écrire les données. Si d'autres threads le définissent pendant que vous écrivez les données, celles-ci resteront définies jusqu'à la prochaine écriture planifiée. J'utiliserais un AtomicBoolean , pour donner à votre thread de persistance une atomicité entre la vérification de l'indicateur et son effacement, comme suit:

private final AtomicBoolean dirty = new AtomicBoolean();

/**
 * Any method that modifies the data structure should set the dirty flag.
 */
public void modify() {
  /* Modify the data first. */
  ...
  /* Set the flag afterward. */
  dirty.set(true);
}

private class Persister extends Thread {
  public void run() {
    while (!Thread.interrupted()) {
      if (dirty.getAndSet(false)) {
        /* The dirty flag was set; this thread cleared it 
         * and should now persist the data. */
         ...
      }
    }
  }
}

Définir un 32 bits (au moins dans .NET) est atomique, mais cela ne vous va pas. Vous devez le lire pour savoir s'il est verrouillé afin de pouvoir le lire. Après la lecture, quelqu'un d'autre le lira avant de pouvoir le définir. Deux threads se retrouvent donc à l'intérieur du fichier "protégé". code. C’est exactement ce à quoi servent les objets de synchronisation (comme la classe .NET Monitor). Vous pouvez également utiliser un Interlocked pour vérifier et incrémenter la variable lock.

Voir aussi: accède à une variable en C # un atome opération?

Dépend beaucoup du matériel et de la machine virtuelle que vous utilisez

Sur certains matériels et certaines machines virtuelles, certaines copies sont atomiques. mais il est plus prudent de supposer que ce n'est pas le cas, même une simple affectation d'entier à entier peut être traduite en quatre instructions machine sur du matériel x856.

Les copies de chaînes et de tableaux peuvent impliquer une séquence de milliers d'instructions. et il est possible que deux threads se mettent à jour simultanément.

Si je comprends bien, pas toujours.

Int32 sera, un Int64 ne sera pas sur un système 32 bits car il a besoin de 2 x 32 bits. Par conséquent, il ne rentre pas dans une cellule 32 bits.

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