web-dev-qa-db-fra.com

Comment @synchronized verrouille / déverrouille dans Objective-C?

Est-ce que @synchronized n'utilise pas "lock" et "unlock" pour obtenir une exclusion mutuelle? Comment ça se verrouille/déverrouille alors?

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];
}
197
David Lin

La synchronisation au niveau du langage Objective-C utilise le mutex, 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 NSLock, vous avez un verrou explicite, tandis qu'avec @synchronized, vous avez un verrou implicite associé à l'objet que vous utilisez pour la synchronisation. L'avantage du verrouillage du niveau de langue réside dans le fait que le compilateur le comprend, ce qui lui permet de traiter les problèmes de portée, mais leur comportement est fondamentalement identique.

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 correct car la transformation réelle est plus complexe et utilise des verrous récursifs, mais elle devrait faire passer le message.

315
Louis Gerbarg

En Objective-C, un bloc @synchronized gère automatiquement le verrouillage et le déverrouillage (ainsi que les exceptions possibles). Le runtime génère essentiellement de manière dynamique un NSRecursiveLock associé à l'objet sur lequel vous synchronisez. Cette Apple documentation l'explique plus en détail. C'est pourquoi vous ne voyez pas les messages du journal de votre sous-classe NSLock - l'objet sur lequel vous synchronisez peut être n'importe quoi, pas seulement un NSLock.

Fondamentalement, @synchronized (...) est une construction pratique qui rationalise votre code. Comme la plupart des abstractions simplificatrices, il y a un surcoût associé (considérez-le comme un coût caché), et il est bon de le savoir, mais les performances brutes ne sont probablement pas le but suprême lorsque de telles constructions sont utilisées.

40
Quinn Taylor

Réellement

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

se 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 disponible depuis iOS 2.0 et importée à l'aide de ...

#import <objc/objc-sync.h>
31
Dirk Theisen

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:

En résumé, il contient un tableau qui mappe les pointeurs d’objet (en utilisant leurs adresses de mémoire comme clés) sur pthread_mutex_t verrous, qui sont verrouillés et déverrouillés au besoin.

3
Raspu