@synchronized lock / unlock ใน Objective-C ทำอย่างไร


201

@ ซิงโครไนซ์ไม่ได้ใช้ "ล็อค" และ "ปลดล็อค" เพื่อให้เกิดการยกเว้นซึ่งกันและกันหรือไม่? มันล็อค / ปลดล็อกได้อย่างไร

ผลลัพธ์ของโปรแกรมต่อไปนี้เป็น "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];
}

หมายเหตุ: เกี่ยวข้องกับstackoverflow.com/questions/1215765
Quinn Taylor

10
คุณไม่จำเป็นต้องแทนที่ init หากคุณไม่ต้องการ รันไทม์จะเรียกใช้งานซูเปอร์คลาสโดยอัตโนมัติหากคุณไม่ได้แทนที่วิธีการ
Constantino Tsarouhas

3
สิ่งสำคัญที่ควรทราบคือรหัสข้างต้นไม่ได้ทำข้อมูลให้ตรงกัน lockวัตถุถูกสร้างขึ้นบนทุกสายจึงมีไม่เคยจะเป็นกรณีที่หนึ่ง@synchronizedล็อคบล็อกออกมาอีก และนี่หมายความว่าไม่มีข้อยกเว้นร่วมกัน) แน่นอนตัวอย่างข้างต้นกำลังดำเนินการในmainดังนั้นจึงไม่มีอะไรที่จะแยกออกไป แต่ไม่มีใครควรคัดลอกรหัสนั้นในที่อื่น
Licks ร้อน

3
หลังจากอ่านหน้า SO ดังนั้นฉันตัดสินใจที่จะตรวจสอบ @ ซิงโครไนซ์อีกเล็กน้อยให้ละเอียดและเขียนโพสต์ในบล็อก คุณอาจพบว่ามีประโยชน์: rykap.com/objective-c/2015/05/09/synchronized
rjkaplan

คำตอบ:


323

การซิงโครไนซ์ระดับภาษา Objective-C ใช้ mutex เช่นเดียวกับที่NSLockทำ ความหมายมีความแตกต่างทางเทคนิคเล็กน้อย แต่โดยทั่วไปแล้วมันถูกต้องที่จะคิดว่ามันเป็นสองอินเตอร์เฟสที่แยกจากกันซึ่งถูกนำมาใช้บนเอนทิตี้ทั่วไป (ดั้งเดิมมากกว่า)

โดยเฉพาะอย่างยิ่งกับNSLockคุณมีการล็อคอย่างชัดเจนในขณะที่@synchronizedคุณมีการล็อคโดยนัยที่เกี่ยวข้องกับวัตถุที่คุณใช้ในการประสาน ประโยชน์ของการล็อคระดับภาษาคือคอมไพเลอร์เข้าใจดีว่ามันสามารถจัดการกับปัญหาการกำหนดขอบเขต แต่โดยทั่วไปแล้วพวกเขาจะทำงานเหมือนกัน

คุณคิดว่า@synchronizedคอมไพเลอร์เขียนใหม่:

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

เปลี่ยนเป็น:

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

ไม่ถูกต้องเนื่องจากการแปลงจริงมีความซับซ้อนมากขึ้นและใช้การล็อคแบบเรียกซ้ำ แต่ควรได้รับการข้าม


17
คุณยังลืมการจัดการข้อยกเว้นที่ @synchronized ทำเพื่อคุณ และเมื่อฉันเข้าใจแล้วสิ่งนี้ได้รับการจัดการที่รันไทม์ นี้จะช่วยให้เพิ่มประสิทธิภาพในการล็อค uncontended ฯลฯ
ควินน์เทย์เลอร์

7
เช่นฉันกล่าวว่าสิ่งที่เกิดขึ้นจริงมีความซับซ้อนมากขึ้น แต่ผมไม่ได้รู้สึกว่าการเขียนคำสั่งส่วนเพื่อสร้างตารางผ่อนคลาย DWARF3 ;-)
หลุยส์ Gerbarg

และฉันไม่สามารถตำหนิคุณได้ :-) นอกจากนี้โปรดทราบว่า OS X ใช้รูปแบบ Mach-O แทน DWARF
Quinn Taylor

5
ไม่มีใครใช้ DWARF เป็นรูปแบบไบนารี OS X ไม่ใช้แคระสำหรับสัญลักษณ์การแก้ปัญหาและจะใช้ตารางผ่อนคลายแคระสำหรับศูนย์ข้อยกเว้นค่าใช้จ่าย
หลุยส์ Gerbarg

7
สำหรับการอ้างอิงฉันได้เขียนคอมไพเลอร์แบ็กเอนด์สำหรับ Mac OS X ;-)
หลุยส์ Gerbarg

40

ใน Objective-C @synchronizedบล็อกจะจัดการกับการล็อกและปลดล็อค (รวมถึงข้อยกเว้นที่เป็นไปได้) โดยอัตโนมัติสำหรับคุณ รันไทม์สร้าง NSRecursiveLock แบบไดนามิกที่เกี่ยวข้องกับวัตถุที่คุณซิงโครไนซ์ เอกสารประกอบของ Apple นี้อธิบายรายละเอียดเพิ่มเติม นี่คือเหตุผลที่คุณไม่เห็นข้อความบันทึกจากคลาสย่อย NSLock ของคุณวัตถุที่คุณซิงโครไนซ์จะเป็นอะไรก็ได้ไม่ใช่แค่ NSLock

โดยพื้นฐานแล้ว@synchronized (...)โครงสร้างความสะดวกสบายที่ปรับปรุงรหัสของคุณ เช่นเดียวกับ abstractions ที่ทำให้เข้าใจง่ายที่สุดมันเกี่ยวข้องกับค่าใช้จ่าย (คิดว่ามันเป็นค่าใช้จ่ายแอบแฝง) และเป็นเรื่องดีที่ควรระวัง แต่ประสิทธิภาพดิบอาจไม่ใช่เป้าหมายสูงสุดเมื่อใช้โครงสร้างดังกล่าวแล้ว


1
ลิงก์นั้นหมดอายุแล้ว นี่คือลิงค์อัพเดท: developer.apple.com/library/archive/documentation/Cocoa/…
Ariel Steiner

31

แท้จริง

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

เปลี่ยนเป็นโดยตรง:

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

API นี้มีให้ตั้งแต่ iOS 2.0 และนำเข้าโดยใช้ ...

#import <objc/objc-sync.h>

ดังนั้นจึงไม่มีการสนับสนุนสำหรับการจัดการข้อยกเว้นโยนอย่างหมดจด?
ดัสติน

เอกสารนี้อยู่ที่ไหนสักแห่ง?
jbat100

6
มีวงเล็บปีกกาที่ไม่สมดุลที่นั่น
Potatoswatter

@Dustin จริง ๆ แล้วมันทำจากเอกสาร: "ในฐานะมาตรการ@synchronizedป้องกันบล็อกจะเพิ่มตัวจัดการข้อยกเว้นลงในรหัสป้องกันโดยปริยายตัวจัดการนี้จะเผยแพร่ Mutex โดยอัตโนมัติในกรณีที่มีข้อยกเว้นเกิดขึ้น"
ปีเตอร์

objc_sync_enter อาจจะใช้ pthread mutex ดังนั้นการแปลงของ Louis นั้นลึกและถูกต้อง
แจ็ค

3

การดำเนินงานของ Apple @synchronized เปิดแหล่งที่มาและมันสามารถพบได้ที่นี่ Mike ash เขียนบทความสองเรื่องที่น่าสนใจเกี่ยวกับเรื่องนี้:

สรุปมันมีตารางที่แมปตัวชี้วัตถุ (ใช้ที่อยู่หน่วยความจำของพวกเขาเป็นกุญแจ) เพื่อpthread_mutex_tล็อคซึ่งจะถูกล็อคและปลดล็อคตามความจำเป็น


-4

เป็นเพียงการเชื่อมโยงสัญญาณกับทุกวัตถุและใช้สิ่งนั้น


ในทางเทคนิคมันสร้างการล็อค mutex แต่แนวคิดพื้นฐานถูกต้อง ดู Diva ของ Apple ได้ที่: developer.apple.com/documentation/Cocoa/Conceptual/ …
Mark Bessey

3
ไม่เพียง แต่เป็น mutex แต่เป็นการล็อคแบบเรียกซ้ำ
kperryua
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.