2009-07-31 2 views
183

@synchronized n'utilise-t-il pas "lock" et "unlock" pour réaliser une exclusion mutuelle? Comment fait-il le verrouillage/déverrouillage alors?Comment @synchronized se verrouille/se déverrouille-t-il dans Objective-C?

La sortie du programme suivant est uniquement "Hello World".

@interface MyLock: NSLock<NSLocking> 
@end 

@implementation MyLock 

- (id)init { 
    return [super init]; 
} 

- (void)lock { 
    NSLog(@"before lock"); 
    [super lock]; 
    NSLog(@"after lock"); 
} 

- (void)unlock { 
    NSLog(@"before unlock"); 
    [super unlock]; 
    NSLog(@"after unlock"); 
} 

@end 


int main (int argc, const char * argv[]) { 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    MyLock *lock = [[MyLock new] autorelease]; 
    @synchronized(lock) { 
     NSLog(@"Hello World"); 
    } 

    [pool drain]; 
} 
+0

Remarque: En relation avec http://stackoverflow.com/questions/1215765/ –

+10

Vous n'avez pas besoin de surcharger init si vous n'en avez pas besoin. L'exécution appelle automatiquement l'implémentation de la superclasse si vous ne remplacez pas une méthode. –

+1

Une chose importante à noter est que le code ci-dessus n'est pas synchronisé. L'objet 'lock' est créé à chaque appel, il n'y aura donc jamais de cas où un bloc' @ synchronized' en verrouille un autre. Et cela signifie qu'il n'y a pas d'exclusion mutuelle.) Bien sûr, l'exemple ci-dessus fait l'opération dans 'main', donc il n'y a rien à exclure de toute façon, mais il ne faut pas copier aveuglément ce code ailleurs. –

Répondre

296

La synchronisation de niveau de langage Objective-C utilise le mutex, tout comme le fait NSLock. Sémantiquement, il existe quelques petites différences techniques, mais il est fondamentalement correct de les considérer comme deux interfaces distinctes implémentées au-dessus d'une entité commune (plus primitive).

En particulier avec un NSLock vous avez un verrou explicite alors qu'avec @synchronized vous avez un verrou implicite associé à l'objet que vous utilisez pour synchroniser. L'avantage du verrouillage de niveau de langue est que le compilateur le comprend afin de pouvoir traiter les problèmes de portée, mais mécaniquement, ils se comportent fondamentalement de la même manière.

Vous pouvez penser @synchronized comme une ré-écriture du compilateur:

- (NSString *)myString { 
    @synchronized(self) { 
    return [[myString retain] autorelease]; 
    } 
} 

est transformé en:

- (NSString *)myString { 
    NSString *retval = nil; 
    pthread_mutex_t *self_mutex = LOOK_UP_MUTEX(self); 
    pthread_mutex_lock(self_mutex); 
    retval = [[myString retain] autorelease]; 
    pthread_mutex_unlock(self_mutex); 
    return retval; 
} 

Ce n'est pas tout à fait exact parce que la réelle transformation est plus complexe et utilise des verrous récursifs, mais il devrait faire passer le message.

+17

Vous oubliez également la gestion des exceptions que @synchronized fait pour vous. Et si je comprends bien, une grande partie de ceci est manipulée à l'exécution. –

+5

Comme je l'ai dit, les choses générées sont plus complexes, mais je n'ai pas envie d'écrire des directives de section pour construire les tables de déroulement DWARF3 ;-) –

+0

Et je ne peux pas blâmer toi. :-) Notez également que OS X utilise le format Mach-O au lieu de DWARF. –

-2

Il associe juste un sémaphore à chaque objet et l'utilise.

+0

Techniquement, il crée un verrou de mutex, mais l'idée de base est correcte. Voir la diva Apple à: http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW16 –

+3

Pas seulement un mutex, mais un verrou récursif. – kperryua

37

En Objective-C, un bloc @synchronized gère automatiquement le verrouillage et le déverrouillage (ainsi que les éventuelles exceptions) pour vous. Le moteur d'exécution génère essentiellement un NSRecursiveLock associé à l'objet sur lequel vous synchronisez. This Apple documentation l'explique plus en détail. C'est pourquoi vous ne voyez pas les messages de journal de votre sous-classe NSLock - l'objet sur lequel vous synchronisez peut être quelque chose, pas seulement un NSLock.

Fondamentalement, @synchronized (...) est une construction pratique qui rationalise votre code. Comme la plupart des abstractions simplificatrices, elle a des frais généraux associés (pensez-y comme un coût caché), et il est bon d'être conscient de cela, mais la performance brute n'est probablement pas le but suprême lorsque vous utilisez de telles constructions.

29

En fait

{ 
    @synchronized(self) { 
    return [[myString retain] autorelease]; 
    } 
} 

transforme directement en:

// needs #import <objc/objc-sync.h> 
{ 
    objc_sync_enter(self) 
    id retVal = [[myString retain] autorelease]; 
    objc_sync_exit(self); 
    return retVal; 
} 

Cette API est disponible depuis iOS 2.0 et ... importés en utilisant

#import <objc/objc-sync.h> 
+0

Il ne fournit donc pas de support pour gérer correctement les exceptions levées? – Dustin

+0

Est-ce documenté quelque part? – jbat100

+6

Il y a une accolade déséquilibrée là. – Potatoswatter

1

la mise en œuvre d'Apple @synchronized est open source et il peut être trouvé here.cendres Mike a écrit deux post très intéressant à ce sujet:

En un mot, il a une table que les cartes objet pointeurs (en utilisant leurs adresses mémoire en tant que touches) pour pthread_mutex_t serrures , qui sont verrouillées et déverrouillées au besoin.

Questions connexes