2009-07-31 11 views
183

Czy @synchronized nie używa "blokady" i "odblokowania", aby osiągnąć wzajemne wykluczenie? Jak działa blokowanie/odblokowywanie?W jaki sposób @synchronized lock/unlock w Objective-C?

Dane wyjściowe tego programu to tylko "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

Uwaga: Powiązane z http://stackoverflow.com/questions/1215765/ –

+10

Nie trzeba przesłonić init, jeśli nie jest to potrzebne. Środowisko wykonawcze automatycznie wywoła implementację nadklasy, jeśli nie zastąpi się metody. –

+1

Należy pamiętać, że powyższy kod nie jest zsynchronizowany. Obiekt 'lock' jest tworzony przy każdym wywołaniu, więc nigdy nie będzie przypadku, w którym jeden blok' @ synchronized' blokuje inny. A to oznacza, że ​​nie ma wzajemnego wykluczenia.) Oczywiście, powyższy przykład wykonuje operację w 'main', więc i tak nie ma nic do wykluczenia, ale nie należy ślepo kopiować tego kodu gdzie indziej. –

Odpowiedz

296

Synchronizacja poziomu języka Objective-C używa muteksu, tak jak robi to NSLock. Semantycznie istnieją pewne niewielkie różnice techniczne, ale zasadniczo słusznie jest myśleć o nich jako o dwóch oddzielnych interfejsach zaimplementowanych na szczycie wspólnego (bardziej prymitywnego) bytu.

W szczególności z NSLock masz jawną blokadę, natomiast przy @synchronized masz niejawną blokadę powiązaną z obiektem, którego używasz do synchronizacji. Zaletą blokowania na poziomie języka jest to, że kompilator ją rozumie, aby poradzić sobie z problemami z zakresu, ale mechanicznie zachowują się w zasadzie tak samo.

Można myśleć o @synchronized jako przepisanie kompilatora:

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

przekształca się:

- (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; 
} 

To nie jest do końca poprawne, ponieważ rzeczywista przekształcać jest bardziej złożona i używa rekurencyjnych zamki, ale powinno to mieć sens.

+17

Zapominasz również o obsłudze wyjątków, którą @synchronized robi dla ciebie. I jak ja to rozumiem, większość z tego jest obsługiwana w czasie wykonywania. Pozwala to na optymalizację niezamontowanych blokad itp. –

+5

Tak jak powiedziałem, rzeczy generowane są bardziej skomplikowane, ale nie miałem ochoty pisać dyrektyw sekcji w celu zbudowania tabel DWARF3 ;-) –

+0

I nie mogę winić ty. :-) Zwróć też uwagę, że OS X używa formatu Mach-O zamiast DWARF. –

-2

Po prostu kojarzy semafor z każdym obiektem i używa go.

+0

Technicznie tworzy blokadę mutex, ale podstawowy pomysł jest poprawny. Zobacz divę Apple na: http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057-CH8-SW16 –

+3

Nie tylko muteks, ale blokada rekurencyjna. – kperryua

37

W Objective-C blok @synchronized automatycznie obsługuje blokowanie i odblokowywanie (jak również możliwe wyjątki). Środowisko wykonawcze dynamicznie generuje w zasadzie blok NSRecursiveLock, który jest powiązany z obiektem, który jest synchronizowany. This Apple documentation wyjaśnia to bardziej szczegółowo. Dlatego nie widzisz komunikatów logów z podklasy NSLock - obiekt, na którym synchronizujesz, może być czymkolwiek, a nie tylko NSLockiem.

Zasadniczo @synchronized (...) jest konstruktem wygoda który usprawnia swój kod. Podobnie jak większość upraszczających abstrakcji, wiąże się ona z kosztami ogólnymi (myślę, że jest to ukryty koszt), i dobrze jest być tego świadomym, ale surowa wydajność prawdopodobnie nie jest nadrzędnym celem przy stosowaniu takich konstrukcji.

29

Właściwie

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

przekształca się bezpośrednio do:

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

tego API dostępny od iOS 2.0 i importować za pomocą ...

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

Nie zapewnia więc wsparcia dla czysto obsługi zgłaszanych wyjątków? – Dustin

+0

Czy to gdzieś dokumentuje? – jbat100

+6

Jest tam niezrównoważony stabilizator. – Potatoswatter

1

realizacja Apple z @synchronized jest open source i można go znaleźć here.Popiół Mike napisał dwa bardzo ciekawy post na ten temat:

w pigułce ma tabelę mapy sprzeciwu wskaźniki (przy użyciu ich adresów pamięci jako klucze) do pthread_mutex_t zamków , które są blokowane i odblokowywane w razie potrzeby.

Powiązane problemy