[iOS Study] 병렬 프로그래밍 가이드 ( operation queue )
Operation Object 에 대한 이야기
-
Operation object 는 NSOperation class 의 인스턴스를 이야기한다.
NSOperation class 는 abstract base class 로 subclass 를 구현해야 한다.
-
NSInvocationOperation 과 NSBlockOperation 이라는 두 가지 형태의 concrete class 도 제공한다.
-
NSInvoationOperation 은 이미 task 를 수행할 function 이 정의되어 있다면 selector 방식으로 해당 function 을 넘기면서 operation 을 생성할 수 있다.
-
NSBlockOperation 은 한개 혹은 그 이상의 block object 를 동시에 수행하도록 할 수도록 하는 operation이다.
여기에 전달된 모든 block 의 수행이 완료되어야 해당 operation 이 완료된 것으로 간주된다.
-
모든 operation object 는 다음과 같은 키 피쳐를 갖는다.
operation 간 graph 기반의 dependency 를 줄 수 있다. 이 dependency 는 엮여있는 모든 operation 이 끝나야 이 operation 을 수행하도록 한다.
추가적인 completion block 을 설정할 수 있다.
KVO notification 을 이용하여 task 의 수행 상태를 감시할 수 있다.
priority 를 설정하여 상대적인 수행 순위를 결정하도록 할 수 있다.
task 수행 중 멈추게 할 수 있는 기능을 추가할 수 있다. ( semantic )
Concurrent vs. Non-concurrent operation
-
보통은 operation 을 만들어 queue 에 넣지만 꼭 그렇게 하지 않아도 된다.
operation 의 start 함수를 바로 호출하여 코드를 수행할 수도 있다.
-
isConcurrent 함수는 operation 이 동기적으로 작동하는지, 비동기적으로 작동하는지를 어떤 thread 에서 start 가 호출되었는지를 기준으로 알려준다.
기본적으로 이 녀석은 NO 를 return 한다.
-
operation 을 수행시키기 위해 thread 를 새로 만들어 그 안에서 start 를 호출할 수 있다.
하지만 보통은 그냥 operation queue 에 넣어주기만 하면 된다.
NSInvocationOperation 만들기
-
NSInvocationOperation 은 전달한 selector 를 수행시키는 기능을 한다.
-
- (NSOperation*) taskWithData:(id)data{
NSInvocationOperation* op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(myTaskMethod:) object:data];
return op;
}
- (void)myTaskMethod:(id)data{
// perform the task
}
NSBlockOperation 만들기
-
NSBlockOperation 은 한개 혹은 그 이상의 block object 를 동시적으로 수행한다.
NSBlockOperation 을 initialize 할 때 한 개 이상의 block 을 전달한다.
-
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// do some work.
}];
-
initialize 후에는 addExecutionBlock: 을 통해 추가 블럭을 전달할 수 있다.
Custom Operation Object 정의하기, 비동기 수행을 위한 operation 설정
-
이 부분은 우선 생략한다.
Operatino 의 수행을 커스터마이징 하기
-
커스터마이징은 queue 에 넣기 전에 수행되어야 한다.
-
dependency 설정을 할 수 있는데 이는 다른 operation object 와의 serial 한 수행을 control 하는 것을 말한다.
dependency 를 설정하면 해당 operation 들이 종료될 때까지 해당 operation 은 수행되지 않는다.
-
dependency 설정은 addDependency 함수를 통해서 한다.
dependency 는 한개의 queue 에 대해 한정되는 것이 아니라, 서로를 서로 다른 queue 에 넣어도 보장된다.
-
circular dependency 는 programmer error 로 해당 operation 들은 절대 작동하지 않게 된다.
-
dependency 는 KVO notification 을 기반으로 작동하기 떄문에
custom operation 을 사용하는 경우에는 KVO notification 처리를 잘 해주어야 한다.
Operation 의 수행 priority 조정하기
-
queue 에 operation 이 추가되면, readiness 를 기반으로 priority 를 결정한다.
priority level 이라는 attribute 가 있어 이 녀석이 더 결정적으로 작동하여 priority 를 결정한다.
기본적으로 normal priority 를 가지고 있지만, setQueuePriority: 함수를 통해 더 높거나 낮은 priority 를 줄 수 있다.
-
readiness 는 dependency 에 의해 결정이 된다.
-
high priority 라도 ready 되지 않은 녀석은 low priority 의 ready 된 녀석보다 늦게 수행된다.
그래서 dependency 와의 상관관계를 잘 알고 있어야 한다.
( dependency 가 priority attribute 보다 우선한다는 것을 알아야 한다. )
Thread 우선순위 바꾸기
-
기본적인 thread priority 는 system kernel 이 관리한다.
하지만 operation 을 통해 조정도 가능하다.
operation 에 0.0 ~ 1.0 사이로 ( 0.0 이 lowest priority ) thread priority 를 줄 수 있다.
명시하지 않으면 기본인 0.5 로 작동한다.
-
setThreadPriority: 함수를 통해서 priority 를 설정할 수 있다.
해당 함수는 queue 에 넣기 전에 불러야 한다.
start 가 불리는 시점에 priority 를 읽어들여 thread priority 를 조정하게 된다.
단, 해당 operation 의 main method 수행이 끝나면 원래의 priority 로 자동으로 돌아가게 된다.
Completion Block 설정하기
-
OS X v10.6 이상에서는 main task 가 끝날 때 불리는 completion block 을 설정할 수 있다.
setCompletionBlock: 함수를 통해 설정할 수 있다.
전달하는 block 은 argument 나 return 이 없어야 한다.
Per-thread storage 를 피하자
-
보통 operation 이 수행되는 thread 는 system 이 만들어준 녀석이다.
그래서 system 이 이 thread 를 어떻게 관리할 지 모르기 때문에,
per-thread storage 를 사용하는 것은 매우 위험한 것이다.
Error 나 Exception 을 다루자
-
OS X v10.6 이상버전에서는 start method 가 exception 을 잡지 않는다.
하지만 그 전 버전에서는 start method 가 exception 을 잡아 suppress 한다. ( 무시한다. )
-
이제는 operation 에서 발생하는 exception 을 handling 해주어야 한다.
그런데 NSOperation 에는 이 exception 을 handling 해주는 callback 따위가 없기 때문에
task code 안에서 알아서 propagate 하는 방법을 고안해서 수행해야 한다.
Operation 을 실행시키자
-
OperationQueue 는 NSOperationQueue class 를 이용하면 된다.
operation queue 갯수에 대한 제약은 없지만, system level 로 내려가면 결국 한번에 수행될 수 있는 operation 의 수에는 제약이 있다.
그래서 operation queue 갯수를 늘리는 것이 더 많은 concurrent job 을 수행하는 것은 아니다.
-
NSOperationQueue *aQueue = [[NSOperationQueue alloc] init];
-
addOpertion: 함수를 통해 operation 을 추가할 수 있다.
addOperations:waitUntilFinished: 함수를 통해 operation 들을 전달할 수도 있고, ( wait arg 는 YES/NO )
addOperationWithBlock: 을 통해 바로 block object 들을 전달할 수도 있다.
-
queue 에 이미 add 된 operation 은 절대 조작하면 안 된다.
queue 에 들어 있는 녀석은 언제 수행될지 모르기 때문에 check and modify 도 위험할 수 있다.
-
setMaxConcurrentOperationCount: 함수를 통해서 한번에 처리할 operation 수를 정할 수도 있다.
여기에 1 값을 전달하게 되면 serial 한 operation queue 를 만드는 것이다.
단, operation 들은 dependency 라던지 다른 무언가 영향을 미치기 떄문에 GCD 의 serial dispatch queue 와는 조금 다른 구현방식을 보일 수 있다는 것을 알아두어야 한다.
Queue 에 넣지 않고 operation 수행하기
-
start method 를 수행하면 된다.
operation 은 isReady 함수가 YES 를 return 할 때까지 돌지 않는다.
dependency 가 있으면 해당 operation 이 종료될 때까지 YES 를 return 하지 않는다.
-
start method 대신 main 이나 다른 method 를 사용하지 말자!!
start 함수는 그냥 수행이 아닌 몇 가지 다른 일을 하기 때문이다.
예를 들면 KVO notification 을 만든다거나..
이미 cancel 된 상태 혹은 excpetion 을 던진 상태라면 수행하지 않는다거나...
Operation 취소하기
-
operation 이 한번 queue 에 들어가면 해당 operation 은 queue 에 소속된 녀석이다.
dequeue 하는 유일한 방법은 cancel 하는 것이다.
-
해당 operation 에 cancel 을 때리거나,
cancelAllOperation 을 queue object 에 때려 모두 취소하는 방법만 가능하다.
Operation suspending 시키기
-
setSuspended: method 를 통해서 잠시 queue 의 작업을 중지시킬 수 있다.
이것이 이미 실행된 operatoin 을 pause 시키거나 그렇지는 않는다.
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[Effecitve Objective-C] #1 Objective-C 의 기원과 친숙해져라. (0) | 2017.07.16 |
---|---|
[iOS Study] 병렬 프로그래밍 가이드 ( dispatch source ) (0) | 2017.07.03 |
[iOS Study] 병렬 프로그래밍 가이드 ( 병렬 앱 디자인 ) (0) | 2017.07.01 |
[iOS] can't return type callbacks for 3 (0) | 2017.06.28 |
[iOS] Java 의 equals 구현하기 (0) | 2017.06.27 |
댓글