1951 Stimmen

Was ist der Unterschied zwischen den atomaren und nichtatomaren Attributen?

Was tun atomic y nonatomic in Eigenschaftserklärungen bedeuten?

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

Was ist der operative Unterschied zwischen diesen drei?

1808voto

bbum Punkte 161596

Die letzten beiden sind identisch; "atomar" ist das Standardverhalten ( beachten Sie, dass es sich nicht um ein Schlüsselwort handelt; es wird nur durch das Fehlen von nonatomic -- atomic wurde in neueren Versionen von llvm/clang als Schlüsselwort hinzugefügt).

Unter der Annahme, dass Sie die Methodenimplementierungen @synthetisieren, ändert sich der generierte Code im Vergleich zu nicht-atomaren Methoden. Wenn Sie Ihre eigenen Setter/Getter schreiben, sind atomic/nonatomic/retain/assign/copy nur beratend. (Anmerkung: @synthesize ist jetzt das Standardverhalten in neueren Versionen von LLVM. Es besteht auch keine Notwendigkeit, Instanzvariablen zu deklarieren; sie werden ebenfalls automatisch synthetisiert und haben eine _ vorangestellt, um einen versehentlichen direkten Zugriff zu verhindern).

Mit "atomic" stellt der synthetisierte Setter/Getter sicher, dass ein ganz Wert wird immer vom Getter zurückgegeben oder vom Setter gesetzt, unabhängig von der Aktivität des Setters auf einem anderen Thread. Das heißt, wenn Thread A in der Mitte des Getters ist, während Thread B den Setter aufruft, wird ein tatsächlich realisierbarer Wert - höchstwahrscheinlich ein automatisch freigegebenes Objekt - an den Aufrufer in A zurückgegeben.

Unter nonatomic werden keine derartigen Garantien gegeben. So, nonatomic ist wesentlich schneller als "atomic".

Was bedeutet "atomar"? no keine Garantien für die Threadsicherheit geben. Wenn Thread A den Getter gleichzeitig mit Thread B und C aufruft, die den Setter mit unterschiedlichen Werten aufrufen, kann Thread A einen der drei zurückgegebenen Werte erhalten - den vor dem Aufruf des Setters oder einen der Werte, die an den Setter in B und C übergeben wurden.

Die Gewährleistung der Datenintegrität - eine der wichtigsten Herausforderungen bei der Programmierung mit mehreren Threads - wird auf andere Weise erreicht.

Hinzu kommt:

atomicity einer einzelnen Eigenschaft kann auch keine Thread-Sicherheit garantieren, wenn mehrere abhängige Eigenschaften im Spiel sind.

Bedenken Sie:

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

In diesem Fall könnte Thread A das Objekt umbenennen, indem er setFirstName: und ruft dann setLastName: . In der Zwischenzeit kann Thread B Folgendes aufrufen fullName zwischen den beiden Aufrufen von Thread A und erhält den neuen Vornamen zusammen mit dem alten Nachnamen.

Um dies zu erreichen, benötigen Sie eine transaktionales Modell . D.h. eine andere Art der Synchronisation und/oder des Ausschlusses, die es erlaubt, den Zugang zu fullName während die abhängigen Eigenschaften aktualisiert werden.

23 Stimmen

Angesichts der Tatsache, dass jeder Thread-sicheren Code seine eigene Sperren usw. tun wird, wann würden Sie wollen atomare Eigenschaft Accessors verwenden? Ich habe Schwierigkeiten, ein gutes Beispiel zu denken.

4 Stimmen

Wenn Sie einen Accessor haben, der eine Struktur (oder ein Objekt) von mehreren Threads aus liest, und Ihr Code gegen die Frage "Ist dies wirklich der aktuelle Zustand" gehärtet ist, ist dies nützlich. D.h. wenn Sie z.B. eine Struktur lesen, die ein paar Einträge hat, die den aktuellen Status zusammenfassen, kann atomares Lesen ein sicheres Lesen ermöglichen.

1 Stimmen

Vergessen Sie es, ich habe die Antwort in einem Kommentar hier gefunden: stackoverflow.com/a/5047416/855738 , made by bbum: "Da der Sperrmechanismus für die Implementierung von Atomic nicht offengelegt ist, gibt es keine Möglichkeit, nur den Setter oder nur den Getter manuell zu implementieren und die Atomizität mit einem @synthetisierten Atomic Setter/Getter zu gewährleisten."

366voto

Louis Gerbarg Punkte 43196

Dies wird erklärt in Apples Dokumentation Im Folgenden finden Sie einige Beispiele dafür, was tatsächlich geschieht.

Beachten Sie, dass es kein Schlüsselwort "atomic" gibt. Wenn Sie nicht "nonatomic" angeben, ist die Eigenschaft atomar, aber die explizite Angabe von "atomic" führt zu einem Fehler.

Wenn Sie nicht "nonatomic" angeben, dann ist die Eigenschaft atomar, aber Sie können in neueren Versionen immer noch explizit "atomar" angeben, wenn Sie das möchten.

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

- (UITextField *) userName {
    return userName;
}

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

Die atomare Variante ist ein wenig komplizierter:

//@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_;
    }
}

Grundsätzlich hat die atomare Version eine Sperre zu nehmen, um Thread-Sicherheit zu garantieren, und ist auch Bumping der ref count auf das Objekt (und die Autorelease-Zählung, um es zu balancieren), so dass das Objekt garantiert für den Aufrufer vorhanden ist, andernfalls gibt es eine potenzielle Race Condition, wenn ein anderer Thread den Wert setzt, wodurch die ref count auf 0 fallen.

Es gibt tatsächlich eine große Anzahl verschiedener Varianten, wie diese Dinge funktionieren, je nachdem, ob es sich bei den Eigenschaften um skalare Werte oder Objekte handelt und wie retain, copy, readonly, nonatomic usw. interagieren. Im Allgemeinen wissen die Eigenschaftssynthetisierer einfach, wie sie das "Richtige" für alle Kombinationen tun.

8 Stimmen

@Louis Gerbarg: Ich glaube, dass Ihre Version des (nichtatomaren, beibehaltenen) Setters nicht richtig funktioniert, wenn Sie versuchen, dasselbe Objekt zuzuweisen (d.h.: userName == userName_)

5 Stimmen

Ihr Code ist etwas irreführend; es gibt keine Garantie, welche atomaren Getter/Setter synchronisiert werden. Kritisch, @property (assign) id delegate; ist nicht auf irgendetwas synchronisiert (iOS SDK GCC 4.2 ARM -Os ), was bedeutet, dass es ein Rennen gibt zwischen [self.delegate delegateMethod:self]; et foo.delegate = nil; self.foo = nil; [super dealloc]; . Siehe stackoverflow.com/questions/917884/

0 Stimmen

@fyolnish Ich bin mir nicht sicher, was _val / val sind, aber nein, nicht wirklich. Der Getter für ein atomares copy / retain Eigenschaft muss sicherstellen, dass sie kein Objekt zurückgibt, dessen Refcount aufgrund des Setters, der in einem anderen Thread aufgerufen wird, null wird, was im Wesentlichen bedeutet, dass sie den Ivar lesen muss, ihn behalten muss, während sie sicherstellt, dass der Setter ihn nicht überschrieben und wieder freigegeben hat, und ihn dann automatisch wieder freigeben muss, um die Beibehaltung auszugleichen. Das bedeutet im Wesentlichen beide die Getter und Setter müssen eine Sperre verwenden (wenn das Speicherlayout festgelegt wäre, sollte dies mit CAS2-Anweisungen möglich sein; leider -retain ist ein Methodenaufruf).

178voto

raw3d Punkte 3405

Atomic

  • ist das Standardverhalten
  • stellt sicher, dass der aktuelle Prozess von der CPU abgeschlossen wird, bevor ein anderer Prozess auf die Variable
  • ist nicht schnell, da es sicherstellt, dass der Prozess vollständig abgeschlossen ist

Nicht-Atomisch

  • ist NICHT das Standardverhalten
  • schneller (für synthetisierten Code, d.h. für Variablen, die mit @property und @synthesize erstellt wurden)
  • nicht thread-sicher
  • kann zu unerwartetem Verhalten führen, wenn zwei verschiedene Prozesse gleichzeitig auf dieselbe Variable zugreifen

143voto

Vijayendra Punkte 1843

Am besten lässt sich der Unterschied anhand des folgenden Beispiels erklären.

Angenommen, es gibt eine atomare String-Eigenschaft namens "Name", und wenn Sie [self setName:@"A"] von Thread A, Aufruf [self setName:@"B"] von Thread B, und rufen Sie [self name] von Thread C, dann werden alle Operationen auf verschiedenen Threads seriell ausgeführt, d.h. wenn ein Thread einen Setter oder Getter ausführt, dann warten andere Threads.

Dies macht die Eigenschaft "name" schreib- und lesesicher, aber wenn ein anderer Thread, D, die [name release] dann kann dieser Vorgang zu einem Absturz führen, da hier kein setter/getter-Aufruf erfolgt. Das bedeutet, dass ein Objekt zwar lese- und schreibsicher ist (ATOMIC), aber nicht thread-sicher, da andere Threads gleichzeitig jede Art von Nachrichten an das Objekt senden können. Der Entwickler sollte die Thread-Sicherheit für solche Objekte sicherstellen.

Wäre die Eigenschaft "name" nicht atomar, würden alle Threads im obigen Beispiel - A, B, C und D - gleichzeitig ausgeführt und ein unvorhersehbares Ergebnis erzielt. Im Falle einer atomaren Ausführung wird entweder A, B oder C zuerst ausgeführt, aber D kann immer noch parallel ausgeführt werden.

120voto

justin Punkte 103032

Die Syntax und Semantik sind bereits in anderen hervorragenden Antworten auf diese Frage gut definiert. Denn Ausführung y Leistung nicht gut beschrieben sind, werde ich meine Antwort hinzufügen.

Was ist der funktionale Unterschied zwischen diesen 3?

Ich hatte Atom als Standard immer als ziemlich merkwürdig angesehen. Auf der Abstraktionsebene, auf der wir arbeiten, ist die Verwendung atomarer Eigenschaften für eine Klasse als Mittel zum Erreichen einer 100 %igen Thread-Sicherheit ein Eckfall. Für wirklich korrekte Multithreading-Programme ist das Eingreifen des Programmierers mit Sicherheit eine Voraussetzung. In der Zwischenzeit sind die Leistungsmerkmale und die Ausführung noch nicht im Detail erforscht worden. Nachdem ich im Laufe der Jahre einige stark multithreading-fähige Programme geschrieben hatte, deklarierte ich meine Eigenschaften als nonatomic die ganze Zeit, weil Atomkraft für keinen Zweck sinnvoll war. Während der Diskussion über die Einzelheiten der atomaren und nichtatomaren Eigenschaften diese Frage habe ich einige Profile erstellt und bin dabei auf einige merkwürdige Ergebnisse gestoßen.

Ausführung

Okay. Das erste, was ich klarstellen möchte, ist, dass die Sperrimplementierung implementierungsdefiniert und abstrahiert ist. Louis verwendet @synchronized(self) in seinem Beispiel - ich habe gesehen, dass dies eine häufige Quelle der Verwirrung ist. Die Implementierung ist nicht eigentlich verwenden. @synchronized(self) ; es verwendet die Objektebene Spinlocks . Die Illustration von Louis eignet sich gut für eine Veranschaulichung auf hohem Niveau unter Verwendung von Konstrukten, die uns allen vertraut sind, aber es ist wichtig zu wissen, dass sie keine @synchronized(self) .

Ein weiterer Unterschied ist, dass atomare Eigenschaften den Zyklus Ihrer Objekte innerhalb des Getters beibehalten/freigeben.

Leistung

Jetzt kommt der interessante Teil: Die Leistung bei atomaren Eigenschaftszugriffen in unbestritten (z.B. Single-Thread) kann in einigen Fällen wirklich sehr schnell sein. In weniger idealen Fällen kann die Verwendung von atomaren Zugriffen mehr als das 20-fache des Overheads von nonatomic . Während die Umstritten Fall mit 7 Threads war 44 Mal langsamer für die Drei-Byte-Struktur (2,2 GHz) Kern i7 Quad Core, x86_64). Die Drei-Byte-Struktur ist ein Beispiel für eine sehr langsame Eigenschaft.

Interessante Randbemerkung: Die benutzerdefinierten Zugriffsfunktionen der Drei-Byte-Struktur waren 52 Mal schneller als die synthetisierten atomaren Zugriffsfunktionen oder 84 % schneller als die synthetisierten nichtatomaren Zugriffsfunktionen.

In strittigen Fällen können die Objekte auch das 50-fache überschreiten.

Aufgrund der Vielzahl der Optimierungen und der unterschiedlichen Implementierungen ist es recht schwierig, die Auswirkungen in der Praxis zu messen. Oft hört man etwas wie "Vertrauen Sie darauf, es sei denn, Sie erstellen ein Profil und stellen fest, dass es ein Problem ist". Aufgrund der Abstraktionsebene ist es tatsächlich recht schwierig, die tatsächlichen Auswirkungen zu messen. Die Ermittlung der tatsächlichen Kosten aus den Profilen kann sehr zeitaufwändig und aufgrund der Abstraktionsebene recht ungenau sein. Auch der Unterschied zwischen ARC und MRC kann einen großen Unterschied ausmachen.

Lassen Sie uns also einen Schritt zurücktreten, no Da wir uns auf die Implementierung von Eigenschaftszugriffen konzentrieren, werden wir die üblichen Verdächtigen einbeziehen, wie objc_msgSend und untersuchen einige praktische Ergebnisse für viele Aufrufe einer NSString Getter in unbestritten Fälle (Werte in Sekunden):

  • MRC | nonatomic | manuell implementierte Getter: 2
  • MRC | nonatomic | synthesized getter: 7
  • MRC | atomic | synthesized getter: 47
  • ARC | nonatomic | synthesized getter: 38 (Hinweis: ARC fügt hier die Anzahl der Referenzen hinzu)
  • ARC | atomic | synthesized getter: 47

Wie Sie wahrscheinlich schon vermutet haben, ist die Referenzzahlaktivität/der Zyklus ein wichtiger Faktor bei Atomkraftwerken und unter ARC. Größere Unterschiede sind auch bei strittigen Fällen zu beobachten.

Obwohl ich sehr auf die Leistung achte, sage ich immer noch Semantik zuerst! . In der Zwischenzeit hat die Leistung bei vielen Projekten eine geringe Priorität. Es kann jedoch nicht schaden, die Ausführungsdetails und Kosten der von Ihnen verwendeten Technologien zu kennen. Sie sollten die richtige Technologie für Ihre Bedürfnisse, Zwecke und Fähigkeiten einsetzen. Hoffentlich erspart Ihnen dies einige Stunden des Vergleichens und hilft Ihnen, bei der Entwicklung Ihrer Programme eine fundierte Entscheidung zu treffen.

0 Stimmen

MRC | atomic | synthesized getter: 47 ARC | atomar | synthetischer Getter: 47 Was macht sie gleich? Sollte ARC nicht mehr Overhead haben?

2 Stimmen

Also, wenn atomaren Eigenschaften sind schlecht y sind sie Standard. Um die Boilerplate-Code zu erhöhen?

0 Stimmen

@LearnCocos2D ich habe soeben auf 10.8.5 auf demselben Rechner getestet, der auf 10.8 ausgerichtet ist, für den unbestrittenen Single-Thread-Fall mit einer NSString die nicht unsterblich ist: -ARC atomic (BASELINE): 100% -ARC nonatomic, synthesised: 94% -ARC nonatomic, user defined: 86% -MRC nonatomic, user defined: 5% -MRC nonatomic, synthesised: 19% -MRC atomic: 102% -- die Ergebnisse sind heute etwas anders. Ich habe keine @synchronized Vergleiche. @synchronized ist semantisch anders, und ich halte es nicht für ein gutes Werkzeug, wenn Sie nicht-triviale nebenläufige Programme haben. Wenn Sie Geschwindigkeit brauchen, vermeiden Sie @synchronized .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X