[ios] 원자 속성과 비 원자 속성의 차이점은 무엇입니까?

재산 신고에서 무엇 atomicnonatomic의미합니까?

@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;

이 세 가지의 운영상의 차이점은 무엇입니까?



답변

마지막 두 개는 동일합니다. “원자”는 기본 동작입니다 ( 실제로는 키워드가 아니며nonatomicatomic , llvm / clang의 최신 버전에서는 키워드로 – 가 추가 되지 않은 경우에만 지정됨).

메소드 구현을 @synthesizing한다고 가정하면 원자 대 비 원자는 생성 된 코드를 변경합니다. 자신의 세터 / 게터를 작성하는 경우 원자 / 비 원자 / 유지 / 할당 / 복사는 단지 자문 일뿐입니다. (참고 : @synthesize는 이제 LLVM의 최신 버전에서 기본 동작입니다. 인스턴스 변수를 선언 할 필요도 없습니다. 인스턴스 변수도 자동으로 합성되며 _우발적 인 직접 액세스를 방지하기 위해 이름 앞에 추가됩니다.)

“원자”를 사용하면 합성 된 setter / getter는 다른 스레드의 setter 활동에 관계없이 getter에서 전체 값이 반환되거나 setter가 설정하도록합니다. 즉, 스레드 A가 게터의 중간에 있고 스레드 B가 세터를 호출하면 실제 실행 가능한 값 (자동 해제 된 개체)이 A의 호출자에게 반환됩니다.

에서 nonatomic그러한 보증은 이루어지지 않습니다. 따라서 nonatomic“원자”보다 상당히 빠릅니다.

“원자”가하지 않는 것은 스레드 안전을 보장하는 것입니다. 스레드 A가 스레드 B와 동시에 getter를 호출하고 C가 다른 값을 가진 setter를 호출하는 경우 스레드 A는 반환 된 세 값 중 하나 또는 설정자에 전달 된 값 중 하나를 리턴 할 수 있습니다. 마찬가지로, 객체는 알 수없는 방식으로 B 또는 C의 값으로 끝날 수 있습니다.

멀티 스레드 프로그래밍의 주요 과제 중 하나 인 데이터 무결성 보장은 다른 방법으로 달성됩니다.

이것에 추가 :

atomicity 단일 속성 중 하나는 여러 종속 속성이 재생 중일 때 스레드 안전성을 보장 할 수 없습니다.

치다:

 @property(atomic, copy) NSString *firstName;
 @property(atomic, copy) NSString *lastName;
 @property(readonly, atomic, copy) NSString *fullName;

이 경우 스레드 A는을 호출 setFirstName:한 다음 호출 하여 객체의 이름을 바꿀 수 있습니다 setLastName:. 그 동안 스레드 B는 fullName스레드 A의 두 호출 사이에서 호출 할 수 있으며 이전 성과 결합 된 새로운 이름을받습니다.

이를 해결하려면 트랜잭션 모델 이 필요합니다 . 즉 fullName, 종속 속성이 업데이트되는 동안 액세스를 제외 할 수있는 다른 종류의 동기화 및 / 또는 제외 .


답변

이것은 Apple의 문서에 설명되어 있지만 아래는 실제로 일어나는 일의 예입니다.

“atomic”키워드가 없으며 “nonatomic”을 지정하지 않으면 속성이 원자 적이지만 “atomic”을 명시 적으로 지정하면 오류가 발생합니다.

“비 원자”를 지정하지 않으면 속성이 원자 적이지만 원하는 경우 최신 버전에서 “원자”를 명시 적으로 지정할 수 있습니다.

//@property(nonatomic, retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    return userName;
}

- (void) setUserName:(UITextField *)userName_ {
    [userName_ retain];
    [userName release];
    userName = userName_;
}

이제 원자 변형은 조금 더 복잡합니다.

//@property(retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {
        retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
      [userName_ retain];
      [userName release];
      userName = userName_;
    }
}

기본적으로 원자 버전은 스레드 안전성을 보장하기 위해 잠금을 수행해야하며 호출자에게 객체가 존재하도록 객체의 참조 횟수와 객체의 균형을 잡기 위해 자동 해제 횟수를 충돌시킵니다. 다른 스레드가 값을 설정하여 참조 횟수가 0으로 떨어지면 잠재적 경쟁 조건입니다.

실제로 속성이 스칼라 값인지 개체인지, 유지, 복사, 읽기 전용, 비 원자 등이 상호 작용하는 방식에 따라 이러한 방식이 작동하는 방식에는 여러 가지 변형이 있습니다. 일반적으로 속성 신디사이저는 모든 조합에 대해 “올바른 일”을 수행하는 방법 만 알고 있습니다.


답변

원자

  • 기본 동작입니다
  • 다른 프로세스가 변수에 액세스하기 전에 현재 프로세스가 CPU에 의해 완료되도록합니다.
  • 프로세스가 완전히 완료되도록 보장하기 때문에 빠르지 않습니다.

비 원자

  • 기본 동작이 아닙니다
  • 더 빠름 (합성 코드, 즉 @property 및 @synthesize를 사용하여 작성된 변수의 경우)
  • 스레드 안전하지 않은
  • 서로 다른 두 프로세스가 동일한 변수에 동시에 액세스 할 때 예기치 않은 동작이 발생할 수 있습니다.

답변

차이점을 이해하는 가장 좋은 방법은 다음 예제를 사용하는 것입니다.

“name”이라는 원자 문자열 특성이 있다고 가정 [self setName:@"A"]하고 스레드 A에서 호출 [self setName:@"B"]하고 스레드 B에서 호출 [self name]하고 스레드 C에서 호출 하면 다른 스레드에 대한 모든 조작이 순차적으로 수행되며 이는 하나의 스레드가 setter를 실행 중임을 의미합니다. 또는 getter이면 다른 스레드가 기다립니다.

이렇게하면 속성 “name”이 (가) 읽기 / 쓰기 안전 상태가되지만 다른 스레드 D가 [name release]동시에 호출 하면 여기에 관련된 setter / getter 호출이 없기 때문에이 작업으로 인해 충돌이 발생할 수 있습니다. 즉, 다른 스레드가 동시에 모든 유형의 메시지를 객체에 보낼 수 있으므로 객체는 읽기 / 쓰기 안전 (ATOMIC)이지만 스레드로부터 안전하지 않습니다. 개발자는 이러한 객체에 대한 스레드 안전성을 보장해야합니다.

“name”속성이 원자가 아닌 경우 위의 예-A, B, C 및 D에있는 모든 스레드가 동시에 실행되어 예기치 않은 결과가 생성됩니다. 원자의 경우 A, B 또는 C 중 하나가 먼저 실행되지만 D는 여전히 병렬로 실행될 수 있습니다.


답변

구문과 의미는 이미이 질문에 대한 다른 훌륭한 답변으로 잘 정의되어 있습니다. 때문에 실행성능을 잘 설명되지 않습니다, 나는 내 대답을 추가합니다.

이 3의 기능적 차이점은 무엇입니까?

나는 항상 원자를 기본적으로 매우 흥미로운 것으로 생각했습니다. 추상화 수준에서 클래스의 원자 속성을 차량으로 사용하여 100 % 스레드 안전성을 달성하는 것이 가장 중요합니다. 진정한 올바른 멀티 스레드 프로그램을 위해서는 프로그래머의 개입이 거의 필요합니다. 한편, 성능 특성 및 실행은 아직 자세히 설명되지 않았습니다. 수년 동안 엄청나게 멀티 스레드 된 프로그램을 작성해온 결과, nonatomic원자는 어떤 목적으로도 감지 할 수 없었기 때문에 내 속성을 전체적으로 선언했습니다 . 이 질문에 대한 원자 및 비 원자 특성의 세부 사항을 논의하는 동안 일부 프로파일 링에서 흥미로운 결과가 발생했습니다.

실행

확인. 가장 먼저 정리하고 싶은 것은 잠금 구현이 구현 정의되고 추상화된다는 것입니다. Louis는 @synchronized(self)그의 예에서 사용합니다 . 나는 이것을 일반적인 혼란의 근원으로 보았습니다. 구현은 실제로 사용 하지 않습니다 @synchronized(self). 객체 레벨 스핀 잠금을 사용합니다 . Louis의 일러스트레이션은 우리 모두에게 친숙한 구문을 사용하는 고급 일러스트레이션에는 적합하지만 사용하지 않는 것이 중요합니다 @synchronized(self).

또 다른 차이점은 원자 속성이 게터 내에서 객체를 유지 / 해제하는 것입니다.

공연

흥미로운 부분은 다음과 같습니다. 경합되지 않은 (예 : 단일 스레드) 경우 에 원자 속성 액세스를 사용하는 성능 은 경우에 따라 매우 빠릅니다. 이상적인 경우보다 적은 경우 원자 액세스를 사용하면 오버 헤드의 20 배 이상이 소요될 수 있습니다 nonatomic. 7 개의 스레드를 사용 하는 컨테스트 된 케이스는 3 바이트 구조 (2.2GHz 코어 i7 쿼드 코어, x86_64)의 경우 44 배 느 렸습니다 . 3 바이트 구조체는 매우 느린 속성의 예입니다.

흥미로운 참고 사항 : 3 바이트 구조체의 사용자 정의 접근자는 합성 원자 접근 자보다 52 배 빠릅니다. 또는 합성 된 비 원자 접근 자의 속도 84 %

분쟁이 발생한 경우의 개체도 50 배를 초과 할 수 있습니다.

구현의 최적화 및 변형이 많기 때문에 이러한 상황에서 실제 영향을 측정하는 것은 매우 어렵습니다. “프로필을 작성하여 문제가 발견되지 않는 한 신뢰하기”와 같은 소리가 들릴 수 있습니다. 추상화 수준으로 인해 실제로 실제 영향을 측정하는 것은 매우 어렵습니다. 프로파일에서 실제 비용을 모으는 데는 시간이 많이 걸리고 추상화로 인해 매우 정확하지 않을 수 있습니다. 또한 ARC 대 MRC는 큰 차이를 만들 수 있습니다.

따라서 속성 액세스 구현에 중점을 두지 않고 뒤로 물러서 봅시다 . 우리는 일반적인 용의자를 포함시키고 objc_msgSend, 경합되지 않은 경우 NSString게터에 대한 많은 호출에 대한 실제 높은 수준의 결과를 조사합니다 (초 단위 값).

  • MRC | 비 원자 | 수동으로 구현 된 게터 : 2
  • MRC | 비 원자 | 합성 게터 : 7
  • MRC | 원자 | 합성 게터 : 47
  • 아크 | 비 원자 | 합성 게터 : 38 (참고 : ARC의 ref 카운트 사이클링 추가)
  • 아크 | 원자 | 합성 게터 : 47

짐작할 수 있듯이, 참조 카운트 활동 / 사이클링은 원자와 ARC 하에서 중요한 기여자입니다. 또한 이의 제기 된 사례에서 더 큰 차이를 볼 수 있습니다.

나는 성능에 세심한주의를 기울이지 만, 여전히 Semantics First 라고 말합니다 ! . 한편, 많은 프로젝트에서 성능은 우선 순위가 낮습니다. 그러나 실행 세부 사항과 사용하는 기술 비용을 아는 것이 확실합니다. 필요, 목적 및 능력에 맞는 기술을 사용해야합니다. 이 방법을 사용하면 몇 시간의 비교 시간을 절약하고 프로그램을 디자인 할 때보다 정확한 결정을 내릴 수 있기를 바랍니다.


답변

원자 = 스레드 안전

비 원자 = 나사산 안전 없음

스레드 안전 :

인스턴스 변수는 런타임 환경에 의한 스레드 실행의 스케줄링 또는 인터리빙에 관계없이 호출 스레드에서 추가 동기화 또는 기타 조정없이 여러 스레드에서 액세스 할 때 올바르게 작동하는 경우 스레드 안전합니다.

우리의 맥락에서 :

스레드가 인스턴스 값을 변경하면 변경된 값을 모든 스레드에서 사용할 수 있으며 한 번에 하나의 스레드 만 값을 변경할 수 있습니다.

사용처 atomic:

다중 스레드 환경에서 인스턴스 변수에 액세스하는 경우

의 의미 atomic:

런타임에서 워치 독 작업이 필요하지 않기 nonatomic때문에 빠르지 nonatomic않습니다.

사용처 nonatomic:

여러 스레드에서 인스턴스 변수를 변경하지 않을 경우 사용할 수 있습니다. 성능이 향상됩니다.


답변

원자 속성과 비 원자 속성에 대한 설명을 여기에서 찾아 냈습니다 . 다음은 동일한 관련 텍스트입니다.

‘원자’는 분류 할 수 없음을 의미합니다. OS / 프로그래밍 용어에서 원자 함수 호출은 중단 할 수없는 호출입니다. 전체 함수를 실행해야하며, 완료 될 때까지 OS의 일반적인 컨텍스트 전환으로 CPU에서 스왑하지 않아야합니다. 알지 못하는 경우를 대비하여 : CPU는 한 번에 하나의 작업 만 수행 할 수 있으므로 OS는 작은 시간 단위로 실행중인 모든 프로세스에 대한 CPU 액세스를 환상으로 전환합니다.멀티 태스킹 CPU 스케줄러는 실행 중 언제라도 (중간 함수 호출에서도) 프로세스를 중단시킬 수 있습니다. 따라서 두 프로세스가 동시에 변수를 업데이트하려고 할 수있는 공유 카운터 변수 업데이트와 같은 작업의 경우 ‘원자 적으로’실행되어야합니다. 즉, 각 프로세스가 다른 프로세스로 교체되기 전에 각 업데이트 작업이 완전히 완료되어야합니다. CPU.

따라서이 경우 원자는 속성 판독기 메소드를 중단 할 수 없다는 것을 의미합니다. 실제로 메소드가 읽은 변수가 다른 스레드 / 호출 / 함수가 가져 오기 때문에 절반의 값을 변경할 수 없음을 의미합니다. CPU로 교체되었습니다.

atomic변수를 인터럽트 할 수 없으므로 어느 시점에서든 변수에 포함 된 값은 (스레드 잠금) 손상되지 않도록 보장되지만이 스레드 잠금을 보장 하면 변수에 대한 액세스 속도가 느려집니다. non-atomic반면에 변수는 그러한 보장을하지 않지만 더 빠른 액세스를 제공합니다. 요약하면 non-atomic여러 스레드가 동시에 변수에 액세스하지 않고 속도를 높일 수 있다는 것을 알고있을 때 계속 하십시오.