Comment @synchronise-t-il le locking / délocking dans Objective-C?

Est-ce que @synchronized n’utilise pas “lock” et “unlock” pour obtenir une exclusion mutuelle? Comment fait-il pour verrouiller / déverrouiller alors?

La sortie du programme suivant est uniquement “Hello World”.

@interface MyLock: NSLock @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]; } 

La synchronisation au niveau du langage Objective-C utilise le mutex, comme le fait NSLock . Sémantiquement, il existe de 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’object que vous utilisez pour la synchronisation. L’avantage du locking au niveau du langage est que le compilateur le comprend pour qu’il puisse gérer les problèmes de scope, mais mécaniquement, ils se comportent essentiellement de la même manière.

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

 - (NSSsortingng *)mySsortingng { @synchronized(self) { return [[mySsortingng retain] autorelease]; } } 

est transformé en:

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

Ce n’est pas tout à fait correct car la transformation réelle est plus complexe et utilise des verrous récursifs, mais elle devrait faire passer le message.

Dans Objective-C, un bloc @synchronized gère @synchronized locking et le délocking (ainsi que les exceptions éventuelles). Le moteur d’exécution génère essentiellement un NSRecursiveLock associé à l’object sur lequel vous effectuez la synchronisation. Cette documentation Apple l’ explique plus en détail. C’est pourquoi vous ne voyez pas les messages de journal de votre sous-classe NSLock – l’object sur lequel vous synchronisez peut être n’importe quoi, pas seulement un NSLock.

À la base, @synchronized (...) est une construction pratique qui simplifie votre code. Comme la plupart des abstractions simplificasortingces, il a associé la surcharge (pensez à un coût caché), et il est bon d’être conscient de cela, mais les performances brutes ne sont probablement pas le but ultime lors de l’utilisation de telles constructions.

Réellement

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

se transforme directement en:

 // needs #import  { objc_sync_enter(self) id retVal = [[mySsortingng retain] autorelease]; objc_sync_exit(self); return retVal; } 

Cette API disponible depuis iOS 2.0 et imscope en utilisant …

 #import  

L’implémentation de @synchronized par Apple est open source et peut être trouvée ici . Mike ash a écrit deux articles très intéressants sur ce sujet:

  • Serrures, sécurité des fils et rapidité
  • Construisons @synchronisé

En résumé, il dispose d’une table qui mappe les pointeurs d’object (en utilisant leurs adresses mémoire en tant que clés) vers les verrous pthread_mutex_t , qui sont verrouillés et déverrouillés selon les besoins.

Il associe simplement un sémaphore à chaque object et l’utilise.