이 블로그 검색

2011년 3월 25일 금요일

Intro to Grand Central Dispatch, Part III: Dispatch Sources



Dispatch Sources

이벤트를 모니터 하기 위한 객체.
이벤트 발생시, 자동으로 dispatch queue상의 특정 block을 실행한다.

이벤트 종류는 다음과 같다(GCD in 10.6.0).

1.Mach port send right state changes.
2.Mach port receive right state changes.
3.External process state change.
4.File descriptor ready for read.
5.File descriptor ready for write.
6.Filesystem node event.
7.POSIX signal.
8.Custom timer.
9.Custom event.
1. Custom event
   dispatch_source_merge_data 함수를 호출하여 자신에게 시그널처리하는 경우 발생한다.
    2가지 종류가 존재 한다.
DISPATCH_SOURCE_TYPE_DATA_ADD
DISPATCH_SOURCE_TYPE_DATA_OR

Custom event 처리 예:
어떠한 작업들의 완료로 진행바를 업데이트하는 작업을 예로 들어보자.

- source 객체를 생성하고(dispatch_source_create),

- 각각의 작업 완료시 호출될 이벤트 핸들러(block/function)을  설정한다
  (dispatch_source_set_event_handler).
  완료 이벤트를 받으면 source객체는 등록된 핸들러(block/function)를  실행해서 진행바를
  업데이트 한다. 이 GUI 업데이트 작업은 main queue에서 처리되어야 하므로 source생성시
  dispatch_get_main_queue를 호출했다.

- 프로그레스바 업데이트를 위한 값을 구하는 것은 글로벌 큐에서 수행된다.
   하나의 작업이 완료될때마다 dispatch_source_merge_data를 호출하고 값으로 1을 전달한다.    
   dispatch_source_merge_data 호출을  하면 source객체의 event handler가 호출된다.
   이벤트 핸들러 에서는 dispatch_source_get_data 함수로 값을 가져올수있다
   (호출하면 값은 0으로 reset됨).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
dispatch_source_t source
  =  dispatch_source_create
  ( DISPATCH_SOURCE_TYPE_DATA_ADD,
   0,
   0,
   dispatch_get_main_queue());
   
 dispatch_source_set_event_handler(source,
     ^{
         [progressIndicator incrementBy: dispatch_source_get_data(source)];
     });      
//dispatch source는 항상 suspended 상태로 시작되기때문에
//생성후 이벤트 처리를 윈하면 resume시켜야한다.
dispatch_resume(source);
    
dispatch_apply([array count], globalQueue,
     ^(size_t index) {         
         // 이부분에서 인덱스로 구한 데이터에 대한 처리 수행
         // .....
         
         //하나의 작업 완료후 이벤트 발생.
         dispatch_source_merge_data(source, 1);
     } );
Pastie #2500389 linked directly from Pastie.
 
dispatch_source_merge_data 호출을 하면 source객체의 event handler가 호출된다.
만약 여러번의 호출이 발생하면 event handler도 여러번 발생할것이다.

그런데 다음 상황을 고려해보자.
GUI 업데이트 작업은 메인 큐에서 수행되는데 만약 메인큐를 처리하는 메인 쓰레드가 다른 작업등으로 인해
바쁜 경우, 메인큐의 작업은 지연되고있을 것이다.
이때 DISPATCH_SOURCE_TYPE_DATA_ADD 타잎의 source 객체인 경우면, 처리되지 못하고
대기중인 여러 이벤트들은 하나로 통합(coalesce) 된다.

그리고 핸들러에서 사용될 값(dispatch_source_merge_data 호출시 넘기는 unsigned long 값)은 dispatch_source_merge_data 호출 할때 마다 계속 더해진다.

이후 메인 쓰레드가 처리가능시점이 되면 event handler가 한번 호출되면서 그동안 누적된 값이
전달된다. dispatch_source_get_data 호출을 하면 값은 다시 0으로 초기화된다.

즉, 이벤트가 바로바로 처리 안되는 경우, 이벤트를 계속 누적시키지 않고 처리가능
시점에 한번만 처리할수 있게 이벤트를 통합하면서 데이터값은 계속 누적시켜 주는 것이다.

하지만 메인 쓰레드가 여유가 있어서 메인 큐에서 바로 바로 가져와 처리 가능하다면,
이벤트 핸들러는 통합처리없이 그때그때마다 수행될것이다.

이벤트 통합을 원하지 않는다면 dispatch source를 사용할 필요는 없다. dispatch_async 호출을 하면 된다.

dispatch_async 대신 dispatch source를 사용하는 오직 하나의 이유라면 바로 이벤트 통합처리 때문이다.

2. Built-In Events  

기본으로 제공되는 이벤트는 어떤것이 있는지 알아본다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DISPATCH_SOURCE_TYPE_READ : file descriptor를 모니터한다.
 
 dispatch_queue_t globalQueue  =  dispatch_get_global_queue
         (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_source_t stdinSource
      = dispatch_source_create  ( DISPATCH_SOURCE_TYPE_READ,
        STDIN_FILENO, //file descriptors
               0,
        globalQueue );
                                
    dispatch_source_set_event_handler ( stdinSource,
     ^{
      char buf[1024];
         int len = read(STDIN_FILENO, buf, sizeof(buf));
         if(len > 0)
             NSLog(@"Got data from stdin: %.*s", len, buf);
     });
     
    dispatch_resume(stdinSource);
Pastie #2500392 linked directly from Pastie.
    
주의할점은 일기 작업이 완료되었다고 해도 dispatch source가 살아있다면 filer 기술자를 close하면 안된다.
만약 다른 source 객체에서 동일한 파일기술자 번호로 생성이 된다면, 그 source 객체는 원하지 않는
읽기가 발생할것이다.

이경우 dispatch_source_set_cancel_handler 함수를 이용한다.
dispatch_source_set_cancel_handler 호출하면서 파일기술자를 닫는 핸들러(block)를 인자로 주고,
그후 dispatch_source_cancel 함수를 호출하여 cancel 핸들러가 호출되게 할수있다.
그럼 파일 기술자가 close될것이다.

좀더 자세한 사항은 apple man 페이지를 참조한다.

DISPATCH_SOURCE_TYPE_TIMER : 주기적으로 이벤트를 발생시킨다.

이 타잎은 좀 특별하다. handle/mask 인자없이 생성되며, 별도의 dispatch_source_set_timer
함수를 이용해서 타이머를 설정한다.

void dispatch_source_set_timer (  dispatch_source_t source,
                              dispatch_time_t start,
                              uint64_t interval,
                                  uint64_t leeway );

leeway:  타이머 정확성을 지정한다. 0 이라면 지정된 interval 을 최대한 정확하게 지켜달라는 의미.
그외 지정되는 숫자는 그 만큼의 interval  증가도 무방하다는 의미이다.
성능향상 및 파워 소비량을 감소 목적.

온전한 내용을 원하신다면 원문을 참조하기 바랍니다...

댓글 없음:

댓글 쓰기