'스레드'에 해당되는 글 1건

  1. 2015.03.18 [iOS]Objective-C로 NSOperation로 멀티스레드 구현하기(2)

저번 스레드글에 이어서 NSOperation에 대해서 조금 덧붙여 이야기 해보고려고 한다. 자세한 내용은 애플 개발자문서에 너무도 자세히 나와 있으므로 여기서는 그냥 이렇게 움직인다라는 설명정도만 하려고 한다.




Cocoa에서는 스레드 병렬처리를 위한 고수준 인터페이스를 제공하고 있다. 뭐냐하면 바로  GCD와NSOperation/NSOperationQueue이 두가지 이다.

이 두가지 방법으로 멀티스레드처리가 가능한데 이 방법의 장점은 직렬큐를 이용하거나(GCD의 경우) 큐의 태스크처리를 지정하는 방법등으로 간단히 골치아픈 데드락을 피할 수 있다. ( 다만 리얼타임처리 요하는 경우에는 스레드를 이용하는편이 낫다. 아주 빠른속도의 처리를 위한 특수한 경우가 해당될듯 하다.)





NSOperation/NSOperationQueue은 내부적으로는 GCD로 구현되어 있다고 한다. 결론적으로 NSOperation/NSOperationQueue는 GCD의Objective-C랩퍼 클래스 인 것이다.

GCD에 비교해서NSOperation/NSOperationQueue가 고수준처리가 가능하다는것은 이전 글에서도 잠깐 언급했지만 얻는만큼 잃는것도 있는법. 그대신 그만큼 오버해드가 발생한다고 한다. 오버해드가 발생할만한 코드를 짜본 적이 없으므로 아직은 잘 모르겠다.

아무튼 그런점의 유념해서 개발시에 GCD를 사용할 것인지를 판단해야 할것이다.


그럼 먼저 NSOperation/NSOperationQueue 에 대해서 설명을 하자면,


NSOperation/NSOperationQueue의 사용방법



NSOperation/NSOperationQueue의 기본적인 개념은 태스크(NSOperation)를 큐(NSOperationQueue)에 추가하여 실행하는 방식으로 설계되어 있다.

코드를 보는편이 이해가 빠를지도..아래의 코드를 보자.


#import <Foundation/Foundation.h>


NSBlockOperation* createAnOperation()

{

    // NSBlockOperation 복수의 블록을 가질 있고 그것을 병렬로 실행 시킬수 있다.

    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock: ^{

        [NSThread sleepForTimeInterval:1];

        NSLog(@"Done: 1");

    }];

    [op addExecutionBlock: ^{

        [NSThread sleepForTimeInterval:1];

        NSLog(@"Done: 2");

    }];

    [op addExecutionBlock: ^{

        [NSThread sleepForTimeInterval:1];

        NSLog(@"Done: 3");

    }];

    return op;

}


int main(int argc, char* argv[])

{

    @autoreleasepool {

        NSArray *ops = @[createAnOperation(), createAnOperation()];

        NSOperationQueue *queue = [[NSOperationQueue allocinit];

        // 여기서 오퍼레이션을 직렬로 실행하기 위해서 오퍼레이션 동시실행수를 1 설정

        [queue setMaxConcurrentOperationCount:1];

        // waitUntilFinishedYES 하면 대기한다.

        [queue addOperations:ops waitUntilFinished:NO];

        // 큐에 직접 블록오퍼레이션을 추가하는 것도 가능하다.

        [queue addOperationWithBlock: ^{

            NSLog(@"All done!");

        }];

        // 전부 오퍼레이션이 끝날때까지 대기.

        [queue waitUntilAllOperationsAreFinished];

    }

    return 0;

}


결과는 아래와 같이 출력된다.

2015-03-17 16:04:47.026 TestApp3[5652:1449633] Done: 1

2015-03-17 16:04:47.026 TestApp3[5652:1449632] Done: 2

2015-03-17 16:04:47.026 TestApp3[5652:1449638] Done: 3

2015-03-17 16:04:48.032 TestApp3[5652:1449632] Done: 3

2015-03-17 16:04:48.032 TestApp3[5652:1449638] Done: 1

2015-03-17 16:04:48.032 TestApp3[5652:1449633] Done: 2

2015-03-17 16:04:48.032 TestApp3[5652:1449632] All done!




NSBlockOperation는 표준으로 준비되어 있는 NSOperation의 자식클래스이다. 설명에는 NSBlockOperation만 예를 들었지만 NSInvocationOperation이라는 Target, Selector를 지정해 줄 수 있는 타입도 있다. 또한 NSOperation를 상속받아 커스텀할 수 있도 있다. 더 알고싶다면 애플 개발자 문서를 참조하자.


NSOperationQueue은 기본적으로 GCD라는 병렬큐이다. 위의 예에서는 setMaxConcurrentOperationCount:1로 설정함으로써 직렬큐처럼(태스크가 하나만 처리되므로 결과적으로는 직렬큐) 사용할 수도 있다. 그와 반대로 복수개의 오퍼레이션을  추가하여 이것을 병렬로도 실행 할 수 있다. 


하나 더 유용한 기능이 있는데 NSOperation은 오퍼레이션간에 의존관계를 설정하는 것도 가능하다. 위의 코드는 직렬관계의 오퍼레이션을 동시에 실행하는수를 제한했지만 아래의 코드는 의존관계를 이용하여 실행하는것도 가능하다.


#import <Foundation/Foundation.h>


int main(int argc, char* argv[])

{

    @autoreleasepool {

        NSOperation *op1 = [NSBlockOperation blockOperationWithBlock: ^{

            [NSThread sleepForTimeInterval:1];

            NSLog(@"Done: 1");

        }];

        NSOperation *op2 = [NSBlockOperation blockOperationWithBlock: ^{

            [NSThread sleepForTimeInterval:1];

            NSLog(@"Done: 2");

        }];

        [op2 addDependency: op1]; // op2op1 의존

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];

        [queue addOperations:@[op1, op2] waitUntilFinished:NO];

        [queue waitUntilAllOperationsAreFinished];

    }

    return 0;

}


여기서는 설명하지 못했지만 각각의 오퍼레이션의 우선도를 설정한다던지, 완료시에 콜백으로 불려지는등등 블록을 설정하는것도 가능하다. 실제로 이 클래스를 사용해보면 NSThread를 이용하는것보다 훨씬 간편하다는것을 알수 있다.

게다가 멀티스레드에서 제일 골치아픈 배타적제어 문제도 직렬큐를 이용하면 간단히 해결할 수 있으므로 일석이조라고 할 수 있겠다.




Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요