>source

Atomic에 대한 여러 게시물을 읽었고 다음과 같이 스레드로부터 안전한지 확인하기 위해 데모를 작성했습니다.

@property(atomic,assign) NSInteger sum;

//그럼 이렇게 하세요

for (NSInteger i= 0; i<1000; i++) {
    dispatch_queue_t queue= dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        self.sum++;
    });
}

속성을 원자 속성으로 "합계"로 만듭니다. 1000개의 동시성 스레드를 시작하여 하나를 추가합니다.

결과는 1000이지만, 그렇지 않습니다. NSLock을 추가하여 self.sum++를 래핑하면 결과는 1000입니다.

이것을 설명할 수 있습니까?

어떤 결과를 얻을 수 있습니까?

rmaddy2021-12-15 18:19:23

그리고 왜 루프가 아니라 루프 내부에 큐를 생성합니까?

rmaddy2021-12-15 18:19:23

997 998 996 1000이 아니라 1000 스레드를 만드는 루프에서

NickYu2021-12-15 18:19:23

dispatch_queue_t queue= dispatch_queue_create... 라인을 루프 앞으로 이동합니다. 각 루프 반복에 대해 새 대기열을 생성하면 안 됩니다. 루프 안에는 dispatch_async만 있어야 합니다.

rmaddy2021-12-15 18:19:23
  • 답변 # 1

    이를 수행하는 또 다른 방법은 속성을 다음 값으로 설정하는 것입니다.블록에서 해당 속성의 값을 즉시 다른 변수에 할당하고 동일한지 확인합니다. 그렇지 않은 경우 값, 예상 값 및 해당 값이 있는 스레드 번호를 표시합니다. 나는 지금 당신과 비슷한 시험에서 그것을 하고 있습니다.

    self.data.intPropertyCheck= myIndex;
    NSInteger capturedPropertyValue= self.data.intPropertyCheck;
    

    dispatch_async를 사용할 때 DISPATCH_QUEUE_CONCURRENT 큐를 사용할 때도 한 스레드가 속성을 밟는 것을 방지하는 방법을 찾지 못했습니다.

    대기열이 주 대기열이면 실제로는 동기 대기열이지만 이는 주 스레드를 차단합니다.

    이것은 많은 비동기 블록을 생성하므로 변경했습니다.에게내 인덱스, 스레드에 이름을 추가하고 이 값을 로깅에 추가하여 콘솔에서 어떤 일이 더 쉽게 일어나는지 볼 수 있고 인덱스 번호에서 검색할 수 있습니다. 여기 내 for 루프가 있습니다.

    int iterations;
    iterations= 1000;
    

    for (int myIndex= 0; myIndex < iterations; myIndex++ ) {
        NSLog(@"Issuing test: %d", myIndex);
        //dispatch_async(queue, ^{
        dispatch_async(queue, ^{
            NSThread *myThread= [NSThread currentThread];
            myThread.name= [NSString stringWithFormat:@"Operation -%d", myIndex];
            self.data.intPropertyCheck= myIndex;
            NSInteger capturedPropertyValue= self.data.intPropertyCheck;
            BOOL success= self.data.intPropertyCheck== myIndex; //By the time we get here, the value from the previous line could have already changed.
            NSLog(@"Test# %d. Expected value: %d", myIndex, myIndex);
            NSLog(@"Test# %d. Actual value: %ld", myIndex, (long)self.data.intPropertyCheck); //This will occasionally show a different value. Why?  Many threads attempting to change the value at once. Even if it is atomic.
            if (success) {
                NSLog(@"Test# %d. Test passed.", myIndex);
            } else {
                NSLog(@"#### Test %d ––––––––––––––––––––––––––––– error start", myIndex);
                NSLog(@"#### Test %d failed. Values do not match.", myIndex);
                NSLog(@"#### Test %d failed. Values do not match. Thread: %@", myIndex, myThread.name);
                NSLog(@"#### Test %d failed. Expected value: %d", myIndex, myIndex);
                NSLog(@"#### Test %d failed. Captured value: %ld. Current value: %ld. Expected value: %d", myIndex, (long)capturedPropertyValue, (long)self.data.intPropertyCheck, myIndex);
                 NSLog(@"#### Test %d ––––––––––––––––––––––––––––– error end", myIndex);
                nil;
                nil;
                NSLog(@"");
              //NSAssert(success, @"#### Test %d failed. Values do not match.", myIndex);
            }
        });
    }
    

    Nil이 포함되어 원하는 경우 중단점을 설정할 수 있습니다.

  • 답변 # 2

    다음을 시도해 보십시오.

    dispatch_queue_t queue= dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    for (NSInteger i= 0; i<1000; i++) {
        dispatch_async(queue, ^{
           NSLock *aLock= [[NSLock alloc] init];
           [aLock lock];
           self.sum++;
           [aLock unlock];
        });
    }
    

    이것은 1000개의 독립적인 잠금을 생성하므로 이것이 실패할 것으로 예상하지 않습니까? 아니면 다른 실패 사례를 예시로 제공하고 있습니까?

    Steve Armstrong2021-12-15 21:40:59

    방금 다른 클래스의 속성을 수정하고 값 집합이 다른 스레드에 의해 방해받지 않는지 확인하여 이것을 테스트했습니다. i 값이 속성에 할당된 다음 블록에서 검색 및 비교되었습니다. 뭔지 맞춰봐. 다른 스레드에 의해 방해될 수 있습니다. 싱글톤에서 NSInteger 속성으로 테스트했습니다.

    Alex Zavatone2021-12-16 11:36:11
  • 답변 # 3

    여기에는 여러 계층이 있습니다.

    첫째, 선언된 속성은 대부분 접근자 메서드 선언에 대한 바로 가기에 불과합니다. 고유한 구현을 제공하지 않는 경우 기본적으로 발생하는 속성의 컴파일러 합성은 속성을 지원하기 위해 해당 메서드와 인스턴스 변수를 정의합니다.

    그래서, 이것은:

    @property(atomic,assign) NSInteger sum;
    

    기본적으로 다음과 같습니다.

    -(NSInteger) sum;
    -(void) setSum:(NSInteger)value;
    

    속성을 합성하면 인스턴스 변수와 이러한 메서드의 구현이 생성됩니다.

    @implementation ...
    {
        NSUInteger _sum;
    }
    -(NSInteger) sum
    {
        //...
    }
    -(void) setSum:(NSInteger)value
    {
        //...
    }
    

    원자 속성의 경우 구현-합집합그리고-setSum:어느 쪽도 다른 쪽을 방해하는 것처럼 보이지 않도록 각각 작동하도록 보장됩니다. 전화-합집합호출과 함께 "동시에" 발생합니다.-setSum:이전 값을 반환하거나-setSum:또는 그 뒤의 값이지만 부분적으로 수정된 일부 프랑켄슈타인 값이나 중간 값은 절대 아닙니다. 마찬가지로 두 개의 동시 호출-setSum:결과는_합집합이러한 호출 중 하나 또는 다른 것에서 값을 갖지만 혼합 또는 중간 값은 절대 갖지 않습니다. 그것은 두 개의 호출이 A 다음 B 또는 B 다음 A 중 임의의 엄격한 순서로 발생한 것처럼 보일 것입니다.

    이는 다음과 같은 복합 유형의 속성에 대해 더 쉽게 이해할 수 있습니다.NSRect. 예를 들어, 속성을 설정하는 두 개의 스레드는 다음과 같은 결과를 낳지 않습니다.기원하나의 스레드와크기저장 중인 다른 스레드에서. 둘 중 하나가 "승리"하고 rect는 일관성이 있습니다. 마찬가지로 getter를 호출하는 스레드는 setter 호출과 동시에 발생하더라도 혼합 값을 절대 볼 수 없습니다.

    다음으로 점 구문을 사용하여 속성에 액세스합니다(예:self.sum)는 실제로 접근자를 호출하기 위한 바로 가기일 뿐입니다. get 및 set 접근자만 있고 "증가" 접근자는 없기 때문에 다음과 같은 문은self.sum++;두 가지를 별도로 수행해야 합니다.

    [self setSum:[self sum] + 1];
    

    따라서 귀하의 진술에는 먼저 전화가 포함됩니다.-합집합그런 다음 전화-setSum:각 스레드에 대해. 다른 스레드가 서로의 작업을 인터리브할 수 없도록 하는 것은 없습니다. 속성의 원자성은 그것을 방지하지 않습니다. 즉, 스레드 A는 호출에서 값 5를 얻을 수 있습니다.-합집합, 스레드 B는 호출에서 값 5를 얻을 수도 있습니다.-합집합, 각각은 6을 새 값으로 계산한 다음 둘 다 호출합니다.-setSum:값이 6입니다. 따라서 두 개의 스레드가 속성을 "증가"하지만 1만 증가합니다.

    요컨대, 원자성은 스레드로부터 안전하지 않습니다. 라고 생각하는 것은 개념적 오류입니다. 바로 원자성입니다. 여러 스레드가 동일한 속성에 동시에 액세스할 때 발생할 수 있는 한 종류의 손상을 방지하지만 모든 종류는 아닙니다.

    개발자가 자체 getter 및 setter를 정의할 때 원자 속성에 대한 추가 설명을 추가할 수 있습니까? 즉, 원자 속성을 선언하고 접근자 구현을 제공하는 경우 여전히 원자 속성이 있습니까?

    Tim Reddy2021-12-15 18:48:11

    @TimReddy, 속성 원자성을 선언하면 접근자를 구현할 때 원자성을 보장할 책임이 있습니다. 일반적으로 컴파일러 생성 접근자가 원자성을 유지하기 위해 사용하는 메커니즘과 상호 운용할 수 없으므로 원자 읽기-쓰기 속성의 접근자 중 하나를 구현하는 경우 두 접근자를 모두 구현해야 합니다. 구현이 선언에서 약속한 원자성을 유지하지 못하면 버그가 있는 것입니다.

    Ken Thomases2021-12-15 18:48:11
  • 이전 .htaccess : PHP 페이지 URL 리디렉션 htaccess PHP
  • 다음 Ghostscript를 사용하여 포함된 파일이 있는 PDF를 PDF/A-3으로 변환