Senkronize, Objective-C'de nasıl kilitlenir / kilitlenir?


201

@Synchronized karşılıklı dışlama sağlamak için "kilit" ve "kilit açma" işlevlerini kullanmıyor mu? Peki o zaman kilitleme / kilit açma nasıl yapılır?

Aşağıdaki programın çıktısı sadece "Merhaba Dünya" dır.

@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];
}


10
İhtiyacınız yoksa init'i geçersiz kılmanız gerekmez. Bir yöntemi geçersiz kılmazsanız, çalışma zamanı otomatik olarak üst sınıfın uygulamasını çağırır.
Constantino Tsarouhas

3
Dikkat edilmesi gereken önemli bir nokta, yukarıdaki kodun senkronize edilmemiş olmasıdır. lockNesne her çağrıda oluşturulur, böylece bir durum olabilir asla nerede bir @synchronizedbaşka dışarı blok kilitler. Ve bu karşılıklı bir dışlama olmadığı anlamına gelir.) Elbette, yukarıdaki örnek işlemi yapıyor, mainyine de hariç tutulacak bir şey yok, ancak bu kodu başka bir yere körü körüne kopyalamamalısınız.
Hot Licks

3
Bu SO sayfasını okuduktan sonra, @synchronized'i biraz daha ayrıntılı bir şekilde araştırmaya ve üzerine bir blog yazısı yazmaya karar verdim. Yararlı bulabilirsiniz: rykap.com/objective-c/2015/05/09/synchronized
rjkaplan

Yanıtlar:


323

Objective-C dil seviyesi senkronizasyonu aynı muteksi kullanır NSLock. Anlamsal olarak bazı küçük teknik farklılıklar vardır, ancak bunları ortak (daha ilkel) bir varlığın üzerine uygulanan iki ayrı arayüz olarak düşünmek temel olarak doğrudur.

Özellikle bir ile NSLockaçık bir kilidiniz varken, @synchronizedsenkronize etmek için kullandığınız nesne ile ilişkili bir örtülü kilit vardır. Dil seviyesi kilitlemenin yararı derleyicinin onu anlamasıdır, böylece kapsam belirleme sorunlarıyla başa çıkabilir, ancak mekanik olarak temelde aynı davranırlar.

@synchronizedDerleyici yeniden yazma olarak düşünebilirsiniz :

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

şu biçime dönüştürülür:

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

Bu tam olarak doğru değildir, çünkü gerçek dönüşüm daha karmaşıktır ve özyinelemeli kilitler kullanır, ancak bu noktaya değinmelidir.


17
Ayrıca, @synchronized'in sizin için yaptığı istisna işlemeyi de unutuyorsunuz. Ve anladığım kadarıyla, bunların çoğu çalışma zamanında ele alınır. Bu, kilitlenmemiş kilitler vb. Optimizasyona olanak tanır
Quinn Taylor

7
Dediğim gibi, üretilen gerçek şeyler daha karmaşık, ama DWARF3 gevşeme tabloları ;-)
Louis Gerbarg

Ve seni suçlayamam. :-) Ayrıca OS X'in DWARF yerine Mach-O formatını kullandığını unutmayın.
Quinn Taylor

5
Hiç kimse ikili biçim olarak DWARF kullanmaz. OS X, hata ayıklama sembolleri için DWARF kullanır ve sıfır maliyet istisnaları için DWARF çözme tablolarını kullanır
Louis Gerbarg

7
Referans olarak, Mac OS X ;-) için derleyici arka uçları yazdım
Louis Gerbarg

40

Objective-C'de, bir @synchronizedblok sizin için otomatik olarak kilitleme ve kilit açma (ve olası istisnalar) ile ilgilenir. Çalışma zamanı, dinamik olarak temelde senkronize ettiğiniz nesne ile ilişkili bir NSRecursiveLock oluşturur. Bu Apple belgeleri bunu daha ayrıntılı olarak açıklamaktadır. Bu yüzden NSLock alt sınıfınızdan günlük mesajlarını görmüyorsunuz - senkronize ettiğiniz nesne sadece bir NSLock değil, herhangi bir şey olabilir.

Temel olarak, @synchronized (...)kodunuzu düzene koyan kullanışlı bir yapıdır. Çoğu basitleştirici soyutlama gibi, ek yükü de (gizli bir maliyet olarak düşünün) ilişkilendirmiştir ve bunun farkında olmak iyidir, ancak bu tür yapıları zaten kullanırken ham performans muhtemelen en büyük amaç değildir.


1
Bu bağlantının süresi doldu. Güncelleştirilmiş bağlantı İşte: developer.apple.com/library/archive/documentation/Cocoa/…
Ariel Steiner

31

Aslında

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

doğrudan dönüştürür:

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

Bu API, iOS 2.0'dan beri mevcut ve ...

#import <objc/objc-sync.h>

Yani atılan istisnaların temiz bir şekilde ele alınması için destek sağlamaz mı?
Dustin

Bu bir yerde belgelendi mi?
jbat100

6
Orada dengesiz bir ayraç var.
Potatoswatter

@Dustin aslında, dokümanlardan şunları yapıyor: "Önlem olarak, @synchronizedblok, korunan koda örtük olarak bir istisna işleyici ekler. Bu işleyici bir istisna atıldığında muteksi otomatik olarak serbest bırakır."
Pieter

objc_sync_enter muhtemelen pthread mutex kullanacaktır, bu yüzden Louis'in dönüşümü daha derin ve doğrudur.
jack


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.