6

In Objective C sto usando un'istanza NSMutableArray da vari thread e sto usando @synchronized per renderlo thread-safe. attualmente tutti i miei accessi a questo array sono protetti da un blocco @synchronized, anche objectAtIndex: method. Ciononostante, mi chiedo quali siano i metodi necessari per essere protetti con @synchronized. devo proteggere l'accesso in lettura?Raccolta NSMutableArray e blocchi @Synchronized

Cosa accadrebbe se "ObjectAtIndex" non fosse protetto e chiamato contemporaneamente a "removeObject"?

Se tutti i metodi sono protetti da un @ sincronizzato per quanto riguarda le prestazioni? (Scrivo un server di gioco tcp/udp e non voglio davvero proteggere troppo questi array se questo diminuisse la perf o generasse i lock).

per esempio Suppongo che il metodo 'containsObject:' enumeri per trovare l'oggetto e che dovrei evitare una chiamata concurent a 'removeObject:' in un altro thread.

Forse una buona soluzione sarebbe quella di avere troppo diverse serrature (per lettura e scrittura) ...

Guida e suggerimento sono i benvenuti!

Grazie mille.

per illustrare Si prega di trovare un codice di esempio riportato di seguito:

@interface TestClass : NSObject 
{ 
    NSMutableArray * array; 
} 

@end 

@implementation TestClass 

- (id)init 
{ 
    self = [super init]; 
    if (self) 
    { 
     array = [NSMutableArray array]; 
    } 
    return self; 
} 

-(id)objectAtIndex:(NSUInteger)index 
{ 
    @synchronized(array) **// IS IT USEFUL OR NOT ??** 
    { 
     return [array objectAtIndex:index]; 
    } 
} 

-(void)removeObject:(id)object 
{ 
    @synchronized(array) 
    { 
     [array removeObject:object]; 
    } 
} 

-(void)compute 
{ 
    @synchronized(array) 
    { 
     for (id object in array) 
     { 
      [object compute]; 
     } 
    } 
} 

@end 

risposta

5

Sì, è necessario sincronizzare lettura accessi per evitare che accada in contemporanea con mutazioni. Gli accessi in lettura possono tranquillamente funzionare contemporaneamente ad altri accessi in lettura.

Se si dispone di più lettori, vale la pena esaminare uno schema di blocco di lettura-scrittura. È possibile utilizzare i blocchi di lettura/scrittura pthread (ad esempio pthread_rwlock_...()).

In alternativa, è possibile utilizzare GCD su OS X 10.7+ e iOS 5+ con le routine di "barriera". Crea una coda concorrente privata. Invia tutte le operazioni di lettura ad esso nel modo normale (ad esempio dispatch_sync()). Invia le operazioni di mutazione ad esso utilizzando una routine di barriera come dispatch_barrier_async(). (Può essere asincrono perché generalmente non è necessario sapere che la mutazione è stata completata prima di continuare. Devi solo sapere che tutte le letture inviate dopo aver inviato la mutazione vedranno i risultati della mutazione e la barriera lo garantisce.)

È possibile ottenere ulteriori informazioni a riguardo se si ha accesso allo WWDC 2011 session video for Session 210 - Mastering Grand Central Dispatch.

+0

Mi piace molto la soluzione GCD. Poiché sto usando molti array mutabili (e anche dizionari) che vorrei proteggere mi chiedo se potrei creare il mio GCDMutableArray e GCDMutableDictionary che sovrascriveranno tutti i metodi e aggiungerò un dispatch_queue_t per ogni istanza di array o dizionario. Ma è una buona soluzione avere 10 dizionari mutabili con il loro dispatch_queue? E creare una coda diversa in ogni istanza? dispatch_queue_create prende un nome come arg, come generare questo nome? Forse questo tipo di array e dict protetti sono già stati scritti ... ma non riescono a trovare nulla. –

+0

Non penso che tali classi siano generalmente utili. Non è quasi mai il caso che si possa raggiungere la sicurezza del filo concentrandosi su classi di basso livello come i contenitori. È quasi certamente necessario analizzare le classi al livello della loro astrazione per * progettare * le loro interfacce, non solo le loro implementazioni, per la sicurezza dei thread. Detto questo, le code di spedizione sono a buon mercato e Apple ti incoraggia a creare un numero ragionevole di design. –

0

È necessario essere consapevoli che ciò che si sta facendo in realtà non aiuta molto. Ad esempio, se l'array contiene dieci elementi e si chiama [myObject objectAtIndex: 9] mentre un altro thread chiama [myObject removeObject: someObject], è probabile che la prima chiamata acceda a un elemento di matrice che non esiste più e genera un'eccezione .

objectAtIndex non è molto utile se altri thread possono rimuovere o inserire oggetti.