2009-07-31 9 views
183

¿@synchronized no usa "bloquear" y "desbloquear" para lograr la exclusión mutua? ¿Cómo se bloquea/desbloquea?¿Cómo se bloquea/desbloquea @synchronized en Objective-C?

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]; 
} 
+0

Nota: relacionado con http://stackoverflow.com/questions/1215765/ –

+10

No es necesario anular init si no lo necesita. El tiempo de ejecución llama automáticamente a la implementación de la superclase si no anula un método. –

+1

Una cosa importante a tener en cuenta es que el código anterior no está sincronizado. El objeto 'lock' se crea en cada llamada, por lo que nunca habrá un caso en el que un bloque' @ synchronized' bloquee a otro. Y esto significa que no hay exclusión mutua.) Por supuesto, el ejemplo anterior está haciendo la operación en 'main', por lo que no hay nada que excluir de todos modos, pero no se debe copiar ciegamente ese código en otro lugar. –

Respuesta

296

La sincronización del nivel de lenguaje Objective-C usa el mutex, al igual que NSLock. Semánticamente hay algunas pequeñas diferencias técnicas, pero es básicamente correcto pensar en ellas como dos interfaces separadas implementadas en la parte superior de 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 del nivel de idioma es que el compilador lo entiende, por lo que puede ocuparse de los problemas del alcance, pero mecánicamente se comportan básicamente de la misma manera.

Se puede 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 real transformación es más compleja y utiliza bloqueos recursivos, pero debe entender el punto.

+17

También olvida el manejo de excepciones que @synchronized hace por usted. Y, como yo lo entiendo, gran parte de esto se maneja en tiempo de ejecución. Esto permite la optimización en bloqueos sin supervisión, etc. –

+5

Como dije, el material generado es más complejo, pero no tenía ganas de escribir directivas de sección para construir las tablas de desenrollado DWARF3 ;-) –

+0

Y no puedo culparme tú. :-) También tenga en cuenta que OS X utiliza el formato Mach-O en lugar de DWARF. –

-2

Simplemente asocia un semáforo con cada objeto y lo usa.

+0

Técnicamente, crea un bloqueo mutex, pero la idea básica es correcta. Vea la diva de Apple en: http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW16 –

+3

No solo un mutex, sino una cerradura recursiva. – kperryua

37

En Objective-C, un bloque @synchronized maneja el bloqueo y desbloqueo (así como posibles excepciones) automáticamente para usted. El tiempo de ejecución genera dinámicamente un NSRecursiveLock que está asociado con el objeto en el que se está sincronizando. This Apple documentation lo explica con más detalle. Es por eso que no está viendo los mensajes de registro de su subclase NSLock: el objeto en el que se sincroniza puede ser cualquier cosa, no solo un bloqueo NS.

Básicamente, @synchronized (...) es una construcción de conveniencia que agiliza su 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 al usar tales construcciones de todos modos.

29

En realidad

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

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 importados usando ...

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

¿Por lo tanto, no proporciona soporte para manejar limpiamente las excepciones lanzadas? – Dustin

+0

¿Está esto documentado en alguna parte? – jbat100

+6

Hay una abrazadera desequilibrada allí. – Potatoswatter

1

implementación de @synchronized de Apple es de código abierto y se puede encontrar here.Mike cenizas escribió dos post muy interesante acerca de este tema:

En pocas palabras, tiene una tabla que asigna objeto punteros (usando sus direcciones de memoria como claves) a pthread_mutex_t cerraduras , que están bloqueados y desbloqueados según sea necesario.

Cuestiones relacionadas