Bagaimana cara @synchronized mengunci / membuka kunci di Objective-C?


201

Apakah @synchronized tidak menggunakan "kunci" dan "membuka" untuk mencapai pengecualian bersama? Bagaimana cara mengunci / membuka kunci?

Output dari program berikut ini hanya "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];
}


10
Anda tidak perlu mengganti init jika Anda tidak membutuhkannya. Runtime secara otomatis memanggil implementasi superclass jika Anda tidak menimpa metode.
Constantino Tsarouhas

3
Yang penting untuk diperhatikan adalah bahwa kode di atas tidak disinkronkan. The lockobjek dibuat pada setiap panggilan, sehingga tidak akan pernah ada kasus di mana satu @synchronizedblok kunci keluar lain. Dan ini berarti tidak ada pengecualian yang saling menguntungkan.) Tentu saja, contoh di atas sedang melakukan operasi main, jadi toh tidak ada yang harus dikecualikan, tetapi kita tidak boleh menyalin secara membabi buta kode itu di tempat lain.
Hot Licks

3
Setelah membaca halaman SO ini, saya memutuskan untuk menyelidiki @sinkronisasi sedikit lebih menyeluruh dan menulis posting blog di atasnya. Anda mungkin merasakan manfaatnya: rykap.com/objective-c/2015/05/09/synchronized
rjkaplan

Jawaban:


323

Sinkronisasi tingkat bahasa Objective-C menggunakan mutex, seperti NSLockhalnya. Semantik ada beberapa perbedaan teknis kecil, tetapi pada dasarnya benar untuk menganggap mereka sebagai dua antarmuka terpisah yang diimplementasikan di atas entitas umum (lebih primitif).

Khususnya dengan NSLockAnda memiliki kunci eksplisit sedangkan dengan @synchronizedAnda memiliki kunci implisit yang terkait dengan objek yang Anda gunakan untuk menyinkronkan. Manfaat dari penguncian tingkat bahasa adalah kompiler memahaminya sehingga dapat menangani masalah pelingkupan, tetapi pada dasarnya mereka berperilaku sama.

Anda dapat menganggapnya @synchronizedsebagai penulisan ulang kompiler:

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

ditransformasikan menjadi:

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

Itu tidak sepenuhnya benar karena transformasi yang sebenarnya lebih kompleks dan menggunakan kunci rekursif, tetapi harus mendapatkan titik di seberang.


17
Anda juga lupa penanganan eksepsi yang dilakukan @sinkronisasi untuk Anda. Dan seperti yang saya mengerti, banyak dari ini ditangani saat runtime. Hal ini memungkinkan untuk optimasi pada kunci uncontended, dll
Quinn Taylor

7
Seperti saya katakan, hal-hal yang dihasilkan sebenarnya lebih kompleks, tetapi saya tidak merasa ingin menulis arahan bagian dalam rangka membangun tabel santai DWARF3 ;-)
Louis Gerbarg

Dan aku tidak bisa menyalahkanmu. :-) Juga catat bahwa OS X menggunakan format Mach-O alih-alih DWARF.
Quinn Taylor

5
Tidak ada yang menggunakan DWARF sebagai format biner. OS X tidak menggunakan DWARF untuk simbol debug, dan ia menggunakan DWARF tabel relung untuk pengecualian biaya nol
Louis Gerbarg

7
Untuk referensi, saya telah menulis backends compiler untuk Mac OS X ;-)
Louis Gerbarg

40

Di Objective-C, sebuah @synchronizedblok menangani penguncian dan pembukaan kunci (serta kemungkinan pengecualian) secara otomatis untuk Anda. Runtime secara dinamis pada dasarnya menghasilkan NSRecursiveLock yang dikaitkan dengan objek yang Anda sinkronkan. Dokumentasi Apple ini menjelaskannya secara lebih rinci. Inilah sebabnya mengapa Anda tidak melihat pesan log dari subkelas NSLock Anda - objek yang Anda sinkronkan bisa apa saja, bukan hanya NSLock.

Pada dasarnya, @synchronized (...)adalah konstruk kenyamanan yang merampingkan kode Anda. Seperti kebanyakan abstraksi yang menyederhanakan, abstraksi ini menghubungkan overhead (menganggapnya sebagai biaya tersembunyi), dan ada baiknya untuk mengetahui hal itu, tetapi kinerja mentah mungkin bukan tujuan tertinggi ketika menggunakan konstruksi tersebut.


1
Tautan itu telah kedaluwarsa. Berikut tautan yang diperbarui: developer.apple.com/library/archive/documentation/Cocoa/…
Ariel Steiner

31

Sebenarnya

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

mentransformasikan langsung menjadi:

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

API ini tersedia sejak iOS 2.0 dan diimpor menggunakan ...

#import <objc/objc-sync.h>

Jadi tidak memberikan dukungan untuk menangani pengecualian yang dilemparkan dengan bersih?
Dustin

Apakah ini didokumentasikan di suatu tempat?
jbat100

6
Ada penyangga yang tidak seimbang di sana.
Potatoswatter

@Dustin sebenarnya melakukannya, dari dokumen: "Sebagai tindakan pencegahan, @synchronizedblok secara implisit menambahkan penangan pengecualian ke kode yang dilindungi. Penangan ini secara otomatis melepaskan mutex jika ada pengecualian yang dilemparkan."
Pieter

objc_sync_enter mungkin akan menggunakan pthread mutex, jadi transformasi Louis lebih dalam dan benar.
jack

3

Implementasi @synchronized dari Apple adalah open source dan dapat ditemukan di sini . Mike ash menulis dua posting yang sangat menarik tentang subjek ini:

Singkatnya ia memiliki tabel yang memetakan pointer objek (menggunakan alamat memori mereka sebagai kunci) untuk pthread_mutex_tmengunci, yang dikunci dan dibuka kunci sesuai kebutuhan.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.