Objective-C에서 @synchronized 잠금 / 잠금 해제 방법은 무엇입니까?


201

@synchronized는 상호 배제를 달성하기 위해 "잠금"및 "잠금 해제"를 사용하지 않습니까? 그러면 어떻게 잠금 / 잠금 해제합니까?

다음 프로그램의 출력은 "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
필요하지 않은 경우 init를 재정의 할 필요가 없습니다. 메소드를 재정의하지 않으면 런타임은 자동으로 슈퍼 클래스 구현을 호출합니다.
Constantino Tsarouhas가

3
중요한 것은 위의 코드가 동기화되지 않았다는 것입니다. lock객체가 모든 호출에 생성되기 때문에 경우가 결코 어디 하나 @synchronized다른 차단 잠금. 그리고 이것은 상호 배제가 없음을 의미합니다.) 물론 위의 예는에서 작업을 수행 main하므로 어쨌든 배제 할 것이 없지만 다른 곳에서는 해당 코드를 맹목적으로 복사해서는 안됩니다.
핫 릭

3
이 SO 페이지를 읽은 후 @synchronized를 좀 더 철저히 조사하고 블로그 게시물을 작성하기로 결정했습니다. 유용 할 것입니다 : rykap.com/objective-c/2015/05/09/synchronized
rjkaplan

답변:


323

Objective-C 언어 레벨 동기화는 뮤텍스를 사용합니다 NSLock. 의미 상 약간의 기술적 차이가 있지만 기본적으로 공통적 인 (보다 원시적 인) 엔티티 위에 구현 된 두 개의 별도 인터페이스로 생각하는 것이 맞습니다.

특히 a 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가 수행하는 예외 처리를 잊어 버렸습니다. 그리고 내가 이해하는 것처럼, 이것의 대부분은 런타임에 처리됩니다. 이것은 비경쟁 잠금 등에 대한 최적화를 가능하게합니다.
Quinn Taylor

7
내가 말했듯이, 실제 생성 된 것들은 더 복잡하지만 DWARF3 unwind 테이블을 빌드하기 위해 섹션 지시문을 작성하는 것처럼 느끼지 않았습니다. ;-)
Louis Gerbarg

그리고 나는 당신을 비난 할 수 없습니다. :-) OS X은 DWARF 대신 Mach-O 형식을 사용합니다.
퀸 테일러

5
DWARF를 이진 형식으로 사용하는 사람은 없습니다. OS X은 디버그 기호에 DWARF를 사용하고 비용이 전혀 들지 않는 경우 DWARF 해제 테이블을 사용합니다.
Louis Gerbarg

7
참고로, 나는 ;-) 맥 OS X에 대한 컴파일러 백엔드를 작성했습니다
루이 Gerbarg

40

Objective-C에서 @synchronized블록은 자동으로 잠금 및 잠금 해제 (및 가능한 예외)를 처리합니다. 런타임은 기본적으로 동기화중인 객체와 연결된 NSRecursiveLock을 동적으로 생성합니다. 이 Apple 설명서 에서 자세히 설명합니다. 이것이 NSLock 서브 클래스에서 로그 메시지를 볼 수없는 이유입니다. 동기화하는 객체는 NSLock뿐만 아니라 무엇이든 될 수 있습니다.

기본적으로 @synchronized (...)코드를 간소화하는 편리한 구조입니다. 가장 단순화 된 추상화와 마찬가지로 오버 헤드 (숨겨진 비용으로 생각)와 관련이 있으며, 그것을 알고있는 것이 좋지만, 그러한 구조를 사용할 때 원시 성능은 아마도 최고의 목표는 아닙니다.


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블록은 암시 적으로 보호 코드에 예외 처리기를 추가합니다.이 처리기는 예외가 발생하는 경우 자동으로 뮤텍스를 해제합니다."
Pieter

objc_sync_enter는 아마도 pthread mutex를 사용할 것이므로 Louis의 변환은 더 깊고 정확합니다.
jack


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.