Domanda Qual è la differenza tra gli attributi atomici e non anatomici?


Cosa fare atomic e nonatomic significa nelle dichiarazioni di proprietà?

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

Qual è la differenza operativa tra questi tre?


1722
2018-02-26 02:31


origine


risposte:


Gli ultimi due sono identici; "atomico" è il comportamento predefinito (nota che non è in realtà una parola chiave; è specificato solo dall'assenza di nonatomic - atomic è stato aggiunto come parola chiave nelle ultime versioni di llvm / clang).

Supponendo che tu stia @sintendendo l'implementazione del metodo, atomico e non atomico cambiano il codice generato. Se stai scrivendo il tuo setter / getter, atomico / non anatomico / conserva / assegna / copia sono solo di consulenza. (Nota: @synthesize è ora il comportamento predefinito nelle versioni recenti di LLVM. Non è inoltre necessario dichiarare variabili di istanza, anche esse verranno sintetizzate automaticamente e avranno un _ anteposto al loro nome per impedire l'accesso diretto accidentale).

Con "atomico", il setter / getter sintetizzato assicurerà che a totale il valore viene sempre restituito dal getter o impostato dal setter, indipendentemente dall'attività del setter su qualsiasi altro thread. Cioè, se il thread A si trova nel mezzo del getter mentre il thread B chiama il setter, un valore reale valido - un oggetto autoreleased, molto probabilmente - verrà restituito al chiamante in A.

In nonatomic, non ci sono garanzie del genere. Così, nonatomic è considerevolmente più veloce di "atomico".

Che cosa "atomico" fa non fare è qualsiasi garanzia sulla sicurezza del filo. Se il thread A chiama il getter contemporaneamente con il thread B e C chiama il setter con valori diversi, il thread A può ottenere uno qualsiasi dei tre valori restituiti - quello prima di ogni setter chiamato o uno dei valori passati nei setter in B e C. Allo stesso modo, l'oggetto può finire con il valore di B o C, non c'è modo di dirlo.

Garantire l'integrità dei dati, una delle principali sfide della programmazione multi-thread, è ottenuto con altri mezzi.

In aggiunta a questo:

atomicity di una singola proprietà non può garantire la sicurezza del thread quando sono in gioco più proprietà dipendenti.

Prendere in considerazione:

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

In questo caso, il thread A potrebbe rinominare l'oggetto chiamando setFirstName: e poi chiamando setLastName:. Nel frattempo, il thread B potrebbe chiamare fullName tra le chiamate A del thread A e riceverà il nuovo nome associato al vecchio cognome.

Per risolvere questo problema, è necessario un modello transazionale. Cioè qualche altro tipo di sincronizzazione e / o esclusione che consente di escludere l'accesso fullName mentre le proprietà dipendenti vengono aggiornate.


1668
2018-02-26 06:40



Questo è spiegato in Apple documentazione, ma di seguito sono riportati alcuni esempi di ciò che sta effettivamente accadendo. Si noti che non esiste una parola chiave "atomica", se non si specifica "nonatomico", la proprietà è atomica, ma specificando "atomico" esplicitamente si verificherà un errore.

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

- (UITextField *) userName {
    return userName;
}

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

Ora, la variante atomica è un po 'più complicata:

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

Fondamentalmente, la versione atomica deve prendere un lock per garantire la sicurezza del thread, e anche il conteggio dei ref sull'oggetto (e il conteggio automatico per bilanciarlo) in modo che l'oggetto sia garantito per il chiamante, altrimenti lì è una potenziale condizione di competizione se un altro thread sta impostando il valore, facendo cadere il conteggio dei ref a 0.

Esistono in realtà un gran numero di diverse varianti di come funzionano queste cose a seconda che le proprietà siano valori o oggetti scalari e come interagire tra conservazione, copia, sola lettura, non anatomica, ecc. In generale i sintetizzatori di proprietà sanno solo come fare la "cosa giusta" per tutte le combinazioni.


342
2018-02-26 06:24



Atomico

  • è il comportamento predefinito
  • assicurerà che il processo presente sia completato dalla CPU, prima che un altro processo acceda alla variabile
  • non è veloce, in quanto garantisce il completamento del processo

Non-Atomic

  • NON è il comportamento predefinito
  • più veloce (per codice sintetizzato, cioè per variabili create usando @property e @synthesize)
  • non thread-safe
  • può comportare un comportamento imprevisto quando due diversi processi accedono contemporaneamente alla stessa variabile

148
2018-05-25 10:56



Il modo migliore per capire la differenza è usare il seguente esempio.

Supponiamo che ci sia una proprietà di stringa atomica chiamata "nome" e se chiami [self setName:@"A"] dal thread A, chiama [self setName:@"B"] dalla discussione B e chiama [self name] dal thread C, quindi tutte le operazioni su thread diversi verranno eseguite in serie, il che significa che se un thread sta eseguendo un setter o un getter, gli altri thread attenderanno.

Questo rende la proprietà "nome" lettura / scrittura sicura, ma se un altro thread, D, chiama [name release] contemporaneamente, questa operazione potrebbe causare un arresto anomalo perché non è presente alcuna chiamata setter / getter. Ciò significa che un oggetto è in lettura / scrittura sicuro (ATOMIC), ma non thread-safe poiché un altro thread può inviare contemporaneamente qualsiasi tipo di messaggio all'oggetto. Lo sviluppatore dovrebbe garantire la sicurezza del thread per tali oggetti.

Se il "nome" della proprietà era nonatomico, tutti i thread nell'esempio precedente - A, B, C e D verranno eseguiti simultaneamente producendo risultati imprevedibili. In caso di atomico, uno di A, B o C verrà eseguito per primo, ma D può ancora essere eseguito in parallelo.


124
2018-01-31 18:36



La sintassi e la semantica sono già ben definite da altre eccellenti risposte a questa domanda. Perché esecuzione e prestazione non sono dettagliato bene, aggiungerò la mia risposta.

Qual è la differenza funzionale tra questi 3?

Avevo sempre considerato l'atomico come predefinito piuttosto curioso. A livello di astrazione a cui lavoriamo, utilizzando le proprietà atomiche per una classe come veicolo per ottenere il 100% di sicurezza del filo è un caso limite. Per i programmi multithread veramente corretti, l'intervento del programmatore è quasi certamente un requisito. Nel frattempo, le caratteristiche e l'esecuzione delle prestazioni non sono state ancora dettagliate in dettaglio. Avendo scritto alcuni programmi fortemente multithread nel corso degli anni, avevo dichiarato le mie proprietà come nonatomicper tutto il tempo perché l'atomico non era sensibile per alcuno scopo. Durante la discussione dei dettagli delle proprietà atomiche e non questa domanda, Ho fatto alcuni profiling incontrato alcuni risultati curiosi.

Esecuzione

Ok. La prima cosa che vorrei chiarire è che l'implementazione del blocco è definita dall'implementazione e astratta. Louis usa @synchronized(self) nel suo esempio: ho visto questo come una fonte comune di confusione. L'implementazione no in realtà uso @synchronized(self); usa il livello dell'oggetto spin lock. L'illustrazione di Louis è buona per un'illustrazione di alto livello usando i costrutti a noi tutti familiari, ma è importante sapere che non usa @synchronized(self).

Un'altra differenza è che le proprietà atomiche manterranno / rilasceranno il ciclo degli oggetti all'interno del getter.

Prestazione

Ecco la parte interessante: prestazioni che utilizzano accessi a proprietà atomiche in incontrastato (ad esempio casi a thread singolo) può essere molto veloce in alcuni casi. In casi meno che ideali, l'uso di accessi atomici può costare più di 20 volte il sovraccarico di nonatomic. Mentre il contestato caso con 7 thread è stato 44 volte più lento per la struttura a tre byte (2,2 GHz core i7 Quad Core, x86_64). La struttura a tre byte è un esempio di una proprietà molto lenta.

Interessante nota a margine: gli accessori definiti dall'utente della struttura a tre byte erano 52 volte più veloci degli accessori atomici sintetizzati; o 84% della velocità degli accessori non atomici sintetizzati.

Gli oggetti nei casi contestati possono anche superare 50 volte.

A causa del numero di ottimizzazioni e variazioni nelle implementazioni, è abbastanza difficile misurare gli impatti del mondo reale in questi contesti. Potresti spesso sentire qualcosa come "Fidati di esso, a meno che non profili e trovi che è un problema". A causa del livello di astrazione, in realtà è abbastanza difficile misurare l'impatto reale. Ridurre i costi effettivi dai profili può richiedere molto tempo e, a causa delle astrazioni, è piuttosto impreciso. Inoltre, ARC vs MRC può fare una grande differenza.

Quindi facciamo un passo indietro, non focalizzandoci sulla realizzazione degli accessi di proprietà, includeremo i soliti sospetti come objc_msgSended esamina alcuni risultati di alto livello nel mondo reale per molte chiamate a a NSString entrare incontrastato casi (valori in secondi):

  • MRC | nonatomico | getter implementati manualmente: 2
  • MRC | nonatomico | getter sintetizzato: 7
  • MRC | atomico | getter sintetizzato: 47
  • ARC | nonatomico | getter sintetizzato: 38 (nota: l'ARC aggiunge il conteggio dei riferimenti ciclicamente qui)
  • ARC | atomico | getter sintetizzato: 47

Come probabilmente avete intuito, l'attività di conteggio dei riferimenti / ciclismo è un contributo significativo con l'atomica e sotto ARC. Vedresti anche maggiori differenze nei casi contestati.

Anche se presta molta attenzione alle prestazioni, dico ancora La semantica prima!. Nel frattempo, le prestazioni sono una bassa priorità per molti progetti. Tuttavia, conoscere i dettagli di esecuzione e i costi delle tecnologie che utilizzi certamente non guasta. Dovresti usare la tecnologia giusta per i tuoi bisogni, scopi e capacità. Speriamo che questo ti risparmi alcune ore di confronto e ti aiuti a prendere una decisione più informata quando progetti i tuoi programmi.


108
2017-08-18 09:47



Atomico= sicurezza del filo

Non-atomica = Nessuna sicurezza del filo

Sicurezza filo:

Le variabili di istanza sono thread-safe se si comportano correttamente quando si accede da più thread, indipendentemente dalla pianificazione o interleaving dell'esecuzione di tali thread da parte dell'ambiente di runtime e senza sincronizzazione aggiuntiva o altro coordinamento sulla parte del codice chiamante.

Nel nostro contesto:

Se un thread modifica il valore dell'istanza, il valore modificato è disponibile per tutti i thread e solo un thread può modificare il valore alla volta.

Dove usare atomic:

se la variabile di istanza sarà accessibile in un ambiente con multithreading.

Implicazione di atomic:

Non veloce come nonatomic perché nonatomic non richiede alcun lavoro di watchdog su questo da runtime.

Dove usare nonatomic:

Se la variabile di istanza non verrà modificata da più thread, puoi usarla. Migliora le prestazioni.


88
2017-07-10 13:07



Ho trovato una spiegazione abbastanza buona delle proprietà atomiche e non atomiche Qui. Ecco un testo rilevante dello stesso:

'atomico' significa che non può essere rotto.   Nei termini di programmazione / sistema operativo, una chiamata di funzione atomica è una che non può essere interrotta: l'intera funzione deve essere eseguita e non sostituita dalla CPU mediante il normale cambio di contesto del sistema operativo fino al suo completamento. Nel caso in cui non lo sapessi: dato che la CPU può fare solo una cosa alla volta, l'OS ruota l'accesso alla CPU a tutti i processi in esecuzione in intervalli di tempo ridotti, per dare illusione di multitasking. Lo scheduler della CPU può (e lo fa) interrompere un processo in qualsiasi momento della sua esecuzione - anche in una chiamata a metà chiamata. Quindi per azioni come l'aggiornamento di variabili contatore condivise in cui due processi potrebbero provare ad aggiornare la variabile allo stesso tempo, devono essere eseguite "atomicamente", cioè ogni azione di aggiornamento deve finire nella sua interezza prima che qualsiasi altro processo possa essere scambiato sul PROCESSORE.

Quindi immagino che atomico in questo caso significhi che i metodi del lettore di attributi non possono essere interrotti - in effetti significa che la variabile (s) che viene letta dal metodo non può cambiare il loro valore a metà perché alcuni altri thread / call / function ottengono scambiato sulla CPU.

Perché il atomic le variabili non possono essere interrotte, il valore contenuto da esse in qualsiasi momento è (thread-lock) garantito incorrotto, sebbene, assicurando che questo blocco del thread renda più lento l'accesso. non-atomic le variabili, d'altra parte, non offrono tale garanzia ma offrono il lusso di un accesso più rapido. Per riassumere, vai con non-atomic quando sai che le tue variabili non saranno accessibili simultaneamente da più thread e accelereranno le cose.


67
2018-02-24 05:17



Dopo aver letto tanti articoli, i messaggi Stack Overflow e le applicazioni demo per controllare gli attributi delle proprietà variabili, ho deciso di mettere insieme tutte le informazioni sugli attributi:

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

Nell'articolo Attributi o modificatori di proprietà variabili in iOS puoi trovare tutti gli attributi sopra menzionati e questo ti aiuterà sicuramente.

  1. atomic

    • atomic significa che solo un thread accede alla variabile (tipo statico).
    • atomic è thread-safe
    • Ma è lento nelle prestazioni
    • atomic è il comportamento predefinito
    • Gli accessor atomici in un ambiente non garbage collection (ad esempio quando si utilizza retain / release / autorelease) utilizzeranno un lock per garantire che un altro thread non interferisca con l'impostazione / acquisizione corretta del valore.
    • In realtà non è una parola chiave.
       

    Esempio:

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

    • nonatomic significa accesso a thread multipli alla variabile (tipo dinamico).
    • nonatomic è thread-safe.
    • Ma è veloce nelle prestazioni
    • nonatomic NON è un comportamento predefinito. Abbiamo bisogno di aggiungere il nonatomic parola chiave nell'attributo della proprietà.
    • Può comportare un comportamento imprevisto quando due diversi processi (thread) accedono alla stessa variabile nello stesso momento.
       

    Esempio:

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

61
2018-03-21 07:10



Prima la risposta più semplice: non c'è differenza tra i tuoi secondi due esempi. Per impostazione predefinita, i metodi di accesso alle proprietà sono atomici.

Gli accessor atomici in un ambiente non garbage collection (ad esempio quando si utilizza retain / release / autorelease) utilizzeranno un lock per garantire che un altro thread non interferisca con l'impostazione / acquisizione corretta del valore.

Vedi il "Prestazioni e discussioni"Sezione della documentazione di Objective-C 2.0 di Apple per ulteriori informazioni e per altre considerazioni sulla creazione di app multi-thread.


52
2018-02-26 02:56



Atomico:

Atomic garantisce che l'accesso alla proprietà sarà eseguito in modo atomico. Per esempio. restituisce sempre un oggetto completamente inizializzato, qualsiasi get / set di una proprietà su un thread deve essere completato prima che un altro possa accedervi.

Se si immagina che la seguente funzione che si verifica su due thread contemporaneamente, si possa capire perché i risultati non sarebbero belli.

-(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;
  }

  ...
}

Professionisti : Il ritorno di oggetti completamente inizializzati ogni volta rende la scelta migliore in caso di multi-threading.

Contro: Colpo di prestazioni, rende l'esecuzione un po 'più lenta

Non Atomico:

A differenza di Atomic, non garantisce ogni volta il ritorno dell'oggetto completamente inizializzato.

Professionisti : Esecuzione estremamente veloce.

Contro: Possibilità di valore spazzatura in caso di multi-threading.


52
2018-02-26 02:41