objective c - ¿Cómo funciona @synchronized lock/unlock en Objective-C?
synchronization (5)
¿@Synchronized no usa "bloquear" y "desbloquear" para lograr la exclusión mutua? ¿Cómo se bloquea / desbloquea entonces?
La salida del siguiente programa es solo "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];
}
Actualmente
{
@synchronized(self) {
return [[myString retain] autorelease];
}
}
se transforma directamente en:
// needs #import <objc/objc-sync.h>
{
objc_sync_enter(self)
id retVal = [[myString retain] autorelease];
objc_sync_exit(self);
return retVal;
}
Esta API disponible desde iOS 2.0 e importada usando ...
#import <objc/objc-sync.h>
En Objective-C, un bloque @synchronized
maneja el bloqueo y desbloqueo (así como las posibles excepciones) automáticamente para usted. El tiempo de ejecución genera de forma dinámica un NSRecursiveLock asociado con el objeto que está sincronizando. Esta documentación de Apple lo explica con más detalle. Esta es la razón por la que no ve los mensajes de registro de su subclase NSLock: el objeto que sincroniza puede ser cualquier cosa, no solo un NSLock.
Básicamente, @synchronized (...)
es una construcción conveniente que agiliza tu código. Como la mayoría de las abstracciones simplificadoras, tiene una sobrecarga asociada (piense en ello como un costo oculto), y es bueno tenerlo en cuenta, pero el rendimiento en bruto probablemente no sea el objetivo supremo cuando se utilicen tales construcciones de todos modos.
La implementación de Apple de @synchronized es de código abierto y se puede encontrar here . Mike Ash escribió dos publicaciones realmente interesantes sobre este tema:
En pocas palabras, tiene una tabla que mapea los punteros de los objetos (usando sus direcciones de memoria como claves) a los bloqueos pthread_mutex_t
, que se bloquean y desbloquean según sea necesario.
La sincronización de nivel de lenguaje de Objective-C usa el mutex, al igual que NSLock
hace NSLock
. Semánticamente hay algunas pequeñas diferencias técnicas, pero es básicamente correcto pensar que son dos interfaces separadas implementadas sobre una entidad común (más primitiva).
En particular, con un NSLock
tiene un bloqueo explícito, mientras que con @synchronized
tiene un bloqueo implícito asociado con el objeto que está utilizando para sincronizar. El beneficio del bloqueo de nivel de idioma es que el compilador lo entiende, por lo que puede lidiar con problemas de alcance, pero mecánicamente se comportan básicamente igual.
Puedes pensar en @synchronized
como una reescritura del compilador:
- (NSString *)myString {
@synchronized(self) {
return [[myString retain] autorelease];
}
}
se transforma 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;
}
Eso no es exactamente correcto porque la transformación real es más compleja y utiliza bloqueos recursivos, pero debería ser lo más importante.
Simplemente asocia un semáforo con cada objeto y lo utiliza.