>

이 코드를 스레드로부터 안전하게 만들고 싶다고 말하자 :

- (void) addThing:(id)thing { // Can be called from different threads
    [_myArray addObject:thing];
}

GCD는 이것을 달성하기 위해 선호되는 방법 인 것 같습니다 :

- (void) addThing:(id)thing { 
    dispatch_sync(_myQueue, ^{  // _myQueue is serial.
        [_myArray addObject:thing];
    });    
}

전통적인 방법에 비해 어떤 이점이 있습니까?

- (void) addThing:(id)thing {
    @synchronized(_myArray) {
        [_myArray addObject:thing];
    }
}


  • 답변 # 1

    dispatch_sync ()가 잠금을 수행하는 나쁜 방법이라는 것을 알았습니다. 중첩 된 통화.

    따라서 직렬 Q에서 dispatch_sync를 호출 한 다음 동일한 Q를 가진 서브 루틴에서 다시 호출 할 수 없습니다. 이는 @synchronized와 전혀 다른 방식으로 동작하지 않음을 의미합니다.

  • 답변 # 2

    좋아, 몇 가지 테스트를 더 수행했으며 결과는 다음과 같습니다.

    잠금 테스트 : 평균 : 2.48661, stdDev : 0.50599

    동기화 된 테스트 : 평균 : 2.51298, stdDev : 0.49814

    디스패치 테스트 : 평균 : 2.17046, stdDev : 0.43199

    그래서 내가 틀렸다, 내 나쁜 :( 누군가 테스트 코드에 관심이 있다면 여기에서 사용할 수 있습니다.

    static NSInteger retCount = 0;
    @interface testObj : NSObject
    @end
    @implementation testObj
    -(id)retain{
        retCount++;
        return [super retain];
    }
    @end
    @interface ViewController : UIViewController{
        NSMutableArray* _a;
        NSInteger _c;
        NSLock* lock;
        NSLock* thlock;
        dispatch_queue_t _q;
    }
    - (IBAction)testBtn:(id)sender;
    @end
    @implementation ViewController
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    }
    -(NSTimeInterval)testCase:(SEL)aSel name:(NSString*)name{
        _a = [[NSMutableArray alloc] init];
        retCount = 0;
        //Sync test
        NSThread* th[10];
        for(int t = 0; t < 10;t ++){
            th[t] = [[NSThread alloc] initWithTarget:self selector:aSel object:nil];
        }
        NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
        for(int t = 0; t < 10;t ++){
            [th[t] start];
        }
        NSInteger thCount = 1;
        while(thCount > 0){
            thCount = 0;
            for(int t = 0; t < 10;t ++){
                thCount += [th[t] isFinished] ? 0 : 1;
            }
        }
        NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate];
        NSLog(@"%@: %2.5f, retainCount:%d, _c:%d, objects:%d", name, end-start, retCount, _c, [_a count]);
        [_a release];
        for(int t = 0; t < 10;t ++){
            [th[t] release];
        }
        return end-start;
    }
    -(void)syncTest{
        for(int t = 0; t < 5000; t ++){
            [self synchronizedAdd:[[[testObj alloc] init] autorelease] ];
        }
    }
    -(void)dispTest{
        for(int t = 0; t < 5000; t ++){
            [self dispatchSyncAdd:[[[testObj alloc] init] autorelease] ];
        }
    }
    -(void)lockTest{
        for(int t = 0; t < 5000; t ++){
            [self lockAdd:[[[testObj alloc] init] autorelease] ];
        }
    }
    
    - (void) synchronizedAdd:(NSObject*)anObject
    {
        @synchronized(self) {
            [_a addObject:anObject];
            _c++;
        }
    }
    - (void) dispatchSyncAdd:(NSObject*)anObject
    {
        dispatch_sync(_q, ^{
            [_a addObject:anObject];
            _c++;
        });
    }
    - (void) lockAdd:(NSObject*)anObject
    {
        [lock lock];
            [_a addObject:anObject];
            _c++;
        [lock unlock];
    }
    - (double)meanOf:(NSArray *)array
    {
        double runningTotal = 0.0;
        for(NSNumber *number in array)
        {
            runningTotal += [number doubleValue];
        }
        return (runningTotal / [array count]);
    }
    - (double)standardDeviationOf:(NSArray *)array
    {
        if(![array count]) return 0;
        double mean = [self meanOf:array];
        double sumOfSquaredDifferences = 0.0;
        for(NSNumber *number in array)
        {
            double valueOfNumber = [number doubleValue];
            double difference = valueOfNumber - mean;
            sumOfSquaredDifferences += difference * difference;
        }
        return sqrt(sumOfSquaredDifferences / [array count]);
    }
    -(void)stats:(NSArray*)data name:(NSString*)name{
        NSLog(@"%@: mean:%2.5f, stdDev:%2.5f", name, [self meanOf:data], [self standardDeviationOf:data]);
    }
    - (IBAction)testBtn:(id)sender {
        _q = dispatch_queue_create("array q", DISPATCH_QUEUE_SERIAL);
        lock = [[NSLock alloc] init];
        NSMutableArray* ltd = [NSMutableArray array];
        NSMutableArray* std = [NSMutableArray array];
        NSMutableArray* dtd = [NSMutableArray array];
        for(int t = 0; t < 20; t++){
            [ltd addObject: @( [self testCase:@selector(lockTest) name:@"lock Test"] )];
            [std addObject: @( [self testCase:@selector(syncTest) name:@"synchronized Test"] )];
            [dtd addObject: @( [self testCase:@selector(dispTest) name:@"dispatch Test"] )];
        }
        [self stats: ltd name:@"lock test"];
        [self stats: std name:@"synchronized test"];
        [self stats: dtd name:@"dispatch Test"];
    }
    @end
    
    

  • 답변 # 3

    몇 가지 사항이 있습니다 : 1) @Synchronize는 일부 모니터에서 무거운 잠금 버전입니다 (개인적으로 NSLock/NSRecursiveLock을 선호합니다) 2) Dispatch_sync가 실행 큐를 빌드 중입니다.

    모두 접근 방식이 비슷한 결과를 낳지 만 수집 스레드를 안전하게 만드는 것과 같은 간단한 솔루션의 경우 1을 선호합니다.

    이유 :

    여러 코어가있는 경우 여러 스레드가 동시에 작동 할 수 있습니다. 스케줄러에 따라 모니터에서 매우 짧은 시간 동안 잠 깁니다.

    새로운 블록을 할당하고 대기열에 넣은 것을 유지하고 (작업 스레드 동기화 됨) 작업 대기열이 준비되면 실행하는 것이 훨씬 가볍습니다.

    두 접근 방식의

    는 매우 다르다.

    어떤 시점에서 콜렉션의 사용량이 많은 것을 발견하면 sync_queue 대신 NSLock과 같은 클래스를 사용하는 경우 리팩토링/변경이 훨씬 간단한 읽기/쓰기 유형으로 변경 잠금을 고려할 수 있습니다.

  • 이전 regex - JavaScript에서 숫자를 형식화하기위한 정규식
  • 다음 SQL Server 용 데이터 생성기?