Question Quelle est la différence entre les attributs atomiques et non atomiques?


Qu'est-ce que atomic et nonatomic signifie dans les déclarations de propriété?

@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;

Quelle est la différence opérationnelle entre ces trois?


1722
2018-02-26 02:31


origine


Réponses:


Les deux derniers sont identiques; "atomic" est le comportement par défaut (notez que ce n'est pas réellement un mot-clé; il est spécifié seulement par l'absence de nonatomic - atomic a été ajouté en tant que mot-clé dans les versions récentes de llvm / clang).

En supposant que vous êtes @synthesizing les implémentations de la méthode, atomic vs non-atomique modifie le code généré. Si vous écrivez vos propres setter / getters, atomic / nonatomic / retain / assign / copy sont simplement consultatifs. (Note: @synthesize est maintenant le comportement par défaut dans les versions récentes de LLVM Il n'est pas non plus nécessaire de déclarer les variables d'instance, elles seront aussi synthétisées automatiquement et auront un _ préfixé à leur nom pour empêcher un accès direct accidentel).

Avec "atomic", le setter / getter synthétisé fera en sorte qu'un entier La valeur est toujours renvoyée par le getter ou définie par le setter, quelle que soit l'activité du paramètre sur tout autre thread. C'est-à-dire que si le thread A est au milieu du getter alors que le thread B appelle le setter, une valeur viable réelle - un objet auto-libéré, très probablement - sera renvoyée à l'appelant dans A.

Dans nonatomic, aucune de ces garanties ne sont faites. Ainsi, nonatomic est considérablement plus rapide que "atomique".

Qu'est-ce que "atomique" ne pas faire est de faire des garanties sur la sécurité du filetage. Si le thread A appelle le getter simultanément avec les threads B et C appelant le setter avec des valeurs différentes, le thread A peut récupérer l'une des trois valeurs renvoyées - celle précédant l'appel des setters ou l'une des valeurs passées dans les setters dans B et C. De même, l'objet peut finir avec la valeur de B ou C, aucun moyen de dire.

Assurer l'intégrité des données - l'un des principaux défis de la programmation multithread - est réalisé par d'autres moyens.

Ajoutant à ceci:

atomicity d'une seule propriété ne peut pas non plus garantir la sécurité des threads lorsque plusieurs propriétés dépendantes sont en jeu.

Considérer:

 @property(atomic, copy) NSString *firstName;
 @property(atomic, copy) NSString *lastName;
 @property(readonly, atomic, copy) NSString *fullName;

Dans ce cas, le thread A pourrait renommer l'objet en appelant setFirstName: puis en appelant setLastName:. En attendant, le thread B peut appeler fullName entre les deux appels du fil A et recevra le nouveau prénom couplé avec l'ancien nom de famille.

Pour y remédier, vous avez besoin d'un modèle transactionnel. C'est à dire. un autre type de synchronisation et / ou d'exclusion qui permet d'exclure l'accès à fullName tandis que les propriétés dépendantes sont mises à jour.


1668
2018-02-26 06:40



Ceci est expliqué dans Apple Documentation, mais voici quelques exemples de ce qui se passe réellement. Notez qu'il n'y a pas de mot-clé "atomic", si vous ne spécifiez pas "nonatomic" alors la propriété est atomique, mais spécifier "atomic" explicitement entraînera une erreur.

//@property(nonatomic, retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    return userName;
}

- (void) setUserName:(UITextField *)userName_ {
    [userName_ retain];
    [userName release];
    userName = userName_;
}

Maintenant, la variante atomique est un peu plus compliquée:

//@property(retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {
        retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
      [userName_ retain];
      [userName release];
      userName = userName_;
    }
}

Fondamentalement, la version atomique doit prendre un verrou afin de garantir la sécurité du fil, et aussi cogner le compte ref sur l'objet (et le compte autorelease pour l'équilibrer) de sorte que l'objet est garanti pour l'appelant, sinon il est une condition de concurrence potentielle si un autre thread est en train de définir la valeur, entraînant la chute du nombre de ref à 0.

Il existe en fait un grand nombre de variantes différentes de la façon dont ces choses fonctionnent, selon que les propriétés sont des valeurs scalaires ou des objets, et comment interagissent retain, copy, readonly, nonatomic, etc. En général, les synthétiseurs de propriété savent juste faire la «bonne chose» pour toutes les combinaisons.


342
2018-02-26 06:24



Atomique

  • est le comportement par défaut
  • s'assurera que le processus actuel est complété par le CPU, avant qu'un autre processus accède à la variable
  • n'est pas rapide, car il garantit que le processus est entièrement terminé

Non-Atomique

  • n'est PAS le comportement par défaut
  • plus rapide (pour le code synthétisé, c'est-à-dire pour les variables créées avec @property et @synthesize)
  • pas thread-safe
  • peut entraîner un comportement inattendu, lorsque deux processus différents accèdent à la même variable en même temps

148
2018-05-25 10:56



La meilleure façon de comprendre la différence consiste à utiliser l'exemple suivant.

Supposons qu'il existe une propriété de chaîne atomique appelée "nom" et si vous appelez [self setName:@"A"] à partir du fil A, appelez [self setName:@"B"] à partir du fil B, et appel [self name] à partir du thread C, toutes les opérations sur des threads différents seront effectuées en série, ce qui signifie que si un thread exécute un setter ou un getter, alors d'autres threads attendront.

Cela rend la propriété "nom" en lecture / écriture sûre, mais si un autre thread, D, appelle [name release] simultanément, cette opération peut provoquer un crash car il n'y a pas d'appel setter / getter impliqué ici. Ce qui signifie qu'un objet est en lecture / écriture sécurisé (ATOMIC), mais pas thread-safe car un autre thread peut simultanément envoyer n'importe quel type de message à l'objet. Le développeur doit garantir la sécurité des threads pour de tels objets.

Si la propriété "nom" était non atomique, alors tous les threads de l'exemple ci-dessus - A, B, C et D s'exécuteront simultanément, produisant un résultat imprévisible. En cas d'atomique, l'un des deux A, B ou C s'exécutera en premier, mais D peut toujours s'exécuter en parallèle.


125
2018-01-31 18:36



La syntaxe et la sémantique sont déjà bien définies par d'autres excellentes réponses à cette question. Car exécution et performance ne sont pas bien détaillés, je vais ajouter ma réponse.

Quelle est la différence fonctionnelle entre ces 3?

J'avais toujours considéré l'atomique comme un défaut assez curieux. Au niveau de l'abstraction, nous travaillons, en utilisant des propriétés atomiques pour une classe comme un véhicule pour atteindre 100% de la sécurité du fil est un cas de coin. Pour les programmes multithread vraiment corrects, l'intervention du programmeur est presque certainement une exigence. Pendant ce temps, les caractéristiques de performance et d'exécution n'ont pas encore été détaillées en détail. Ayant écrit quelques programmes fortement multithread au cours des années, j'avais déclaré mes propriétés comme nonatomictout le temps parce que l'atomique n'était sensé pour aucun but. Au cours de la discussion des détails des propriétés atomiques et non atomiques cette question, J'ai fait quelques profils rencontrés des résultats curieux.

Exécution

D'accord. La première chose que je voudrais clarifier est que l'implémentation de verrouillage est définie et implémentée par l'implémentation. Louis utilise @synchronized(self) dans son exemple, j'ai vu cela comme une source commune de confusion. La mise en œuvre ne réellement utilisation @synchronized(self); il utilise le niveau de l'objet verrous de spin. L'illustration de Louis est bonne pour une illustration de haut niveau utilisant des constructions que nous connaissons tous, mais il est important de savoir qu'elle n'utilise pas @synchronized(self).

Une autre différence est que les propriétés atomiques conservent / libèrent vos objets dans le getter.

Performance

Voici la partie intéressante: Performance utilisant des accès de propriété atomique dans incontesté (par exemple, un seul thread) peut être vraiment très rapide dans certains cas. Dans des cas moins qu'idéaux, l'utilisation d'accès atomiques peut coûter plus de 20 fois les frais généraux nonatomic. Tandis que le Contesté cas utilisant 7 threads était 44 fois plus lent pour la structure à trois octets (2,2 GHz Core i7 Quad Core, x86_64). La structure à trois octets est un exemple de propriété très lente.

Note intéressante: Les accesseurs définis par l'utilisateur de la structure à trois octets étaient 52 fois plus rapides que les accesseurs atomiques synthétisés; ou 84% de la vitesse des accesseurs non atomiques synthétisés.

Les objets dans les cas contestés peuvent également dépasser 50 fois.

En raison du nombre d'optimisations et de variations dans les implémentations, il est assez difficile de mesurer les impacts réels dans ces contextes. Vous pourriez souvent entendre quelque chose comme «Faites-le confiance, à moins que vous ne fassiez un profil et que vous trouviez que c'est un problème». En raison du niveau d'abstraction, il est en réalité assez difficile de mesurer l'impact réel. Le glanage des coûts réels à partir des profils peut prendre beaucoup de temps et, en raison des abstractions, être très imprécis. De même, ARC vs MRC peut faire une grande différence.

Alors reculons, ne pas en mettant l'accent sur la mise en œuvre des accès de propriété, nous inclurons les suspects habituels comme objc_msgSend, et examiner certains résultats de haut niveau réels pour de nombreux appels à un NSString getter dans incontesté cas (valeurs en secondes):

  • MRC | nonatomique | getters implémentés manuellement: 2
  • MRC | nonatomique | getter synthétisé: 7
  • MRC | atomique | getter synthétisé: 47
  • ARC | nonatomique | getter synthétisé: 38 (note: l'ARC ajoute le compte de l'arbitre cycliste ici)
  • ARC | atomique | getter synthétisé: 47

Comme vous l'avez probablement deviné, l'activité de comptage de référence / cyclisme est un contributeur significatif avec les atomes et sous ARC. Vous verriez également de plus grandes différences dans les cas contestés.

Bien que je porte une attention particulière à la performance, je dis toujours Sémantique d'abord!. Pendant ce temps, la performance est une faible priorité pour de nombreux projets. Cependant, connaître les détails d'exécution et les coûts des technologies que vous utilisez ne fait aucun mal. Vous devez utiliser la bonne technologie pour vos besoins, vos objectifs et vos capacités. J'espère que cela vous permettra d'économiser quelques heures de comparaisons et de vous aider à prendre une décision plus éclairée lors de la conception de vos programmes.


108
2017-08-18 09:47



Atomique= sécurité du filetage

Non-atomique = Pas de sécurité du filetage

Sécurité du filetage:

Les variables d'instance sont thread-safe si elles se comportent correctement à partir de plusieurs threads, indépendamment de la planification ou de l'entrelacement de l'exécution de ces threads par l'environnement d'exécution et sans synchronisation supplémentaire ou autre coordination de la part du code appelant.

Dans notre contexte:

Si un thread modifie la valeur de l'instance, la valeur modifiée est disponible pour tous les threads, et un seul thread peut changer la valeur à la fois.

Où utiliser atomic:

si la variable d'instance est accessible dans un environnement multithread.

Implication de atomic:

Pas aussi vite que nonatomic car nonatomic ne nécessite pas de travail de surveillance sur ce à partir de l'exécution.

Où utiliser nonatomic:

Si la variable d'instance ne va pas être modifiée par plusieurs threads, vous pouvez l'utiliser. Cela améliore les performances.


89
2017-07-10 13:07



J'ai trouvé une explication assez bien posée des propriétés atomiques et non-atomiques ici. Voici un texte pertinent de la même:

«atomique» signifie qu'il ne peut pas être décomposé.   En termes d'OS / programmation, un appel de fonction atomique est un appel qui ne peut pas être interrompu - toute la fonction doit être exécutée, et non échangée hors de la CPU par le changement de contexte habituel du système d'exploitation jusqu'à ce qu'il soit terminé. Juste au cas où vous ne le saviez pas: puisque le CPU ne peut faire qu'une seule chose à la fois, le système d'exploitation fait tourner l'accès au CPU à tous les processus en cours dans des tranches de temps, pour donner le illusion de multitâche. Le planificateur de CPU peut (et interrompt) un processus à n'importe quel moment de son exécution - même en cas d'appel de fonction moyenne. Ainsi, pour des actions comme la mise à jour de variables de comptage partagées où deux processus peuvent essayer de mettre à jour la variable en même temps, ils doivent être exécutés "atomiquement", c'est-à-dire que chaque action de mise à jour doit être terminée. CPU.

Donc, je devinerais que atomique dans ce cas signifie que les méthodes de lecture d'attributs ne peuvent pas être interrompues - ce qui signifie que les variables lues par la méthode ne peuvent pas changer leur valeur à mi-chemin parce qu'un autre thread / appel / fonction échangé sur le CPU.

Parce que le atomic les variables ne peuvent pas être interrompues, la valeur qu'elles contiennent à tout moment est (thread-lock) garantie d'être non corrompu, bien que, assurant ce verrouillage de thread rend l'accès à eux plus lent. non-atomic D'un autre côté, les variables n'offrent pas cette garantie mais offrent le luxe d'un accès plus rapide. Pour résumer, allez avec non-atomic Lorsque vous savez que vos variables ne seront pas accessibles par plusieurs threads simultanément et accélérer les choses.


67
2018-02-24 05:17



Après avoir lu tant d'articles, Stack Overflow affiche et fait des applications de démonstration pour vérifier les attributs des propriétés des variables, j'ai décidé de mettre toutes les informations d'attributs ensemble:

  1. atomic             // Défaut
  2. nonatomic
  3. strong = retain        // Défaut
  4. weak = unsafe_unretained
  5. retain
  6. assign             // Défaut
  7. unsafe_unretained
  8. copy
  9. readonly
  10. readwrite                 // Défaut

Dans l'article Attributs de propriété de variable ou modificateurs dans iOS vous pouvez trouver tous les attributs mentionnés ci-dessus, et cela va certainement vous aider.

  1. atomic

    • atomic signifie qu'un seul thread accède à la variable (type statique).
    • atomic est thread safe.
    • Mais il est lent en performance
    • atomic est le comportement par défaut
    • Les accesseurs atomiques dans un environnement non collecté par les ordures (c'est-à-dire lors de l'utilisation de conserver / libérer / autorelease) utiliseront un verrou pour s'assurer qu'un autre thread n'interfère pas avec le réglage / obtention correct de la valeur.
    • Ce n'est pas réellement un mot-clé.
       

    Exemple:

        @property (retain) NSString *name;
    
        @synthesize name;
    
  2. nonatomic

    • nonatomic signifie que plusieurs threads accèdent à la variable (type dynamique).
    • nonatomic est thread-dangereux.
    • Mais c'est rapide en performance
    • nonatomic Ce n'est PAS un comportement par défaut. Nous devons ajouter le nonatomic mot-clé dans l'attribut de propriété.
    • Cela peut entraîner un comportement inattendu, lorsque deux processus différents (threads) accèdent à la même variable en même temps.
       

    Exemple:

        @property (nonatomic, retain) NSString *name;
    
        @synthesize name;
    

61
2018-03-21 07:10



La réponse la plus simple en premier: Il n'y a pas de différence entre vos deux seconds exemples. Par défaut, les accesseurs de propriété sont atomiques.

Les accesseurs atomiques dans un environnement non collecté par les ordures (c'est-à-dire lors de l'utilisation de conserver / libérer / autorelease) utiliseront un verrou pour s'assurer qu'un autre thread n'interfère pas avec le réglage / obtention correct de la valeur.

Voir le "Performance et filetage"section de la documentation Objective-C 2.0 d'Apple pour plus d'informations et d'autres considérations lors de la création d'applications multi-thread.


52
2018-02-26 02:56



Atomique

Atomic garantit que l'accès à la propriété sera effectuée de manière atomique. Par exemple. il renvoie toujours un objet entièrement initialisé, tout get / set d'une propriété sur un thread doit se terminer avant qu'un autre puisse y accéder.

Si vous imaginez la fonction suivante se produisant sur deux threads à la fois, vous pouvez voir pourquoi les résultats ne seraient pas jolis.

-(void) setName:(NSString*)string
{
  if (name)
  {
    [name release]; 
    // what happens if the second thread jumps in now !?
    // name may be deleted, but our 'name' variable is still set!
    name = nil;
  }

  ...
}

Avantages : Le retour des objets entièrement initialisés à chaque fois fait le meilleur choix en cas de multi-threading.

Les inconvénients : Performance hit, rend l'exécution un peu plus lente

Non-Atomique:

Contrairement à Atomic, il ne garantit pas le retour de l'objet entièrement initialisé à chaque fois.

Avantages : Exécution extrêmement rapide.

Les inconvénients : Les chances de la valeur de la poubelle en cas de multi-threading.


52
2018-02-26 02:41