2021년 2월 11일 목요일

[IOS] GCD, BLOCK 사용 Dispatch Queues 비동기 큐 처리

 Grand Central Dispatch(GCD)는 큐를 동기/비동기로 처리할수 있도록 도와주는 도구다. 쓰레드를 os 가 자동관리 및 분배해준다. 개발자는 큐에 태스크를 생성해서 넣어주면 OS가 자동으로 태스크들을 실행해주는 방식이다.

일단  자바의 쓰레드에 비해 사용하기 쉽다는 거다.

  • Serial Queue : 순서대로 큐의 태스크를 실행한다. 한개가 끝나면 다음 태스크가 실행되는 방식이다. 단 큐는 여러개 만들수 있기 때문에 여러개의 큐에서 동시에 여러태스크를 동시에 (Concurrency)실행할수 있다.
  • Concurrent Dispatch Queue: GDQ라고 불리며 , 여러태스트를 하나의 큐에서 동시에 진행한다. 동시라고 해서 일시에 시작하는건 아니고 큐에 푸시한 순서대로 동시에 실행되는 거다.
Block Syntax

(처리블럭)을 이용하여 콜백함수처럼 사용하는   블럭코딩은 다음과 같이 한다.

    ^{

        NSLog(@"I am Block");

    };;


    void (^myBlock)(void) = ^{

        NSLog(@"This is a Block");

    };

    // invoke

    myBlock();




블럭 생성과 함수에서 람다(인터페이스? 입급함수?) 처럼 이용해보자.

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view.

    UBPayInterface *interface = [UBPayInterface sharedInstance:FALSE];


    BOOL isInitialized = [interface isInitalized1];


    //---- 1.선언방식

    ^{

        NSLog(@"I am Block");

    };;

    

    // 변수에 대입방법

    void (^myBlock)(void) = ^{

        NSLog(@"This is a Block");

    };

    // 사용

    myBlock();

    

    //---- 2.변수에 파라메터와 리턴값

    // 리턴값 (^변수이름)(파라메터들) = ^(파라메터들) { 리턴값 } 형식이다.

    NSString* (^getParameterAndReturnString)(NSString*, BOOL) = ^(NSString* name, BOOL isTrush){

        NSString* helloYour = nil;

        if (!isTrush) {

            helloYour = [NSString stringWithFormat:@"%@, is a Liar", name];

        }

        

        return helloYour;

    };

    // 사용

    NSLog(@"%@", getParameterAndReturnString(@"kim", FALSE));

    

    //---- 3.블럭은 {} 스코프안에서 처리되기 때문에 외부의 변수를 기본적으로 읽을수 없다. 때문에 __block 키워드를 사용하여 블럭스코프외부의 변수에 제한적으로 접근할수 있다.

    __block int outerVal = 100;

    void (^canReadableOuterVal)(void) = ^(void){

        outerVal = 50;

    };

    // 사용

    canReadableOuterVal();

    NSLog(@"%d", outerVal);

    

    

    //---- 4.2 호출할때는 선언된 파라메터와 같은 형식으로 호출하면 된다.

    //Java 인터페이스라면  useBlockParameterEx(new myInterface() { @Override sumSum(int num1, int num2) {NSLog(@"%d", num1 + num2);} })

    [self useBlockParameterEx:^(int num1, int num2) {

        NSLog(@"%d", num1 + num2);

    }];

    

    

    NSLog(@"%@", isInitialized ? @"초기화 성공" : @"초기화 실패" );

}


//---- 4.1 함수의 파라메터로 선언은 이렇게 한다.(람다처럼)

//Java 인터페이스라면  useBlockParameterEx(MyInterface argMyinterface) { argMyinterface.sumSum(10, 20); }

- (void) useBlockParameterEx: (void (^)(int, int)) sumSum {

    sumSum(10, 20);

}


Dispatch Queues 디스패치큐 .

디스패치큐는 dispatch_get_main_queue(프로그램에서 사용하는 메인큐 싱글, 즉 시리얼 디스패치큐) 와 dispatch_get_global_queue(시스템에서 사용하는 동시성 큐, 콘커런트 큐) 와 프로그래머 직접 만드는 dispatch_queue_create(이 명령으로  쓰레드를 프로그래머만든다) 
이렇게 3종류가 있다.
  1. dispatch_get_global_queue (시스템에서관리)
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 형태로서 우선순위에 의해 High, Default, Low, Background 로 구분할수 있다.
  2. dispatch_get_main_queue (우리 앱에 메인 쓰레드)
  3. dispatch_queue_create (사용자가 생성한 쓰레드)
    dispatch_queue_create("this area is for a log", DISPATCH_QUEUE_SERIAL) 형태이며DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_CONCURRENT 두가지 생성옵션과 sync, Async 실행방식으로 구분된다.
A. Serial Dispatch Queue 생성 
순차적으로 실행해야 되는 작업은  DISPATCH_QUEUE_SERIAL옵션으로 큐를 만들고 , 작업을 큐에 넣으면 된다.

  


sync - 릴레이 처럼 출발한 순서대로 

    //---- 5. Serial 큐.

    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);

    dispatch_sync(queue, ^{ NSLog(@"-> 1"); });

    dispatch_sync(queue, ^{ NSLog(@"-> 2"); });

    dispatch_sync(queue, ^{ NSLog(@"-> 3"); });

    

    //---- 5-1. Serial 큐 지연시키기.

    dispatch_time_t pushTime3s = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));

    dispatch_sync(queue, ^{ NSLog(@"-> 5"); });

    dispatch_after(pushTime3s,queue, ^{ NSLog(@"-> 6"); });

    

    //---- 5-2. Serial 큐 지연시키기.

    // 일단 블럭시키고 시간이 걸리는 작업을 하자 8->6 이 예상되지만, 블럭킹처리시간으로 인해 큐에 추가되는 순서는 6->8

    // 시간지연을 주더라도 해당시간에 실행되는것이아니라 해당시간에 큐에 추가하려고 하는거다.

    // 따라서 긴시간 작업하던 7의 중간에 6이 끼어들어추가하려해서 추가됬고, 8이 그다음에 추가된거다.주의해야한다.

    typedef void (^iambloking)(void);

    iambloking blokinghehe = ^{

    NSMutableArray *array = [[NSMutableArray alloc] init];

    for ( long i = 0; i < 100000000; i++ ) {

        [array addObject:[NSNumber numberWithLong:i]];

    }};

    dispatch_sync(queue, ^{ NSLog(@"-> 7");blokinghehe();  }); // 오래걸리는 블럭킹을 부르자

    dispatch_sync(queue, ^{ NSLog(@"-> 8"); });

    //1,2,3,4,5,7(wait),6,8


Async - 마라톤처럼 도착한 순서대로 

    //---- 5. Serial 큐.

    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);

    dispatch_async(queue, ^{ NSLog(@"-> 1"); });

    dispatch_async(queue, ^{ NSLog(@"-> 2"); });

    dispatch_async(queue, ^{ NSLog(@"-> 3"); });

    

    //---- 5-1. Serial 큐 지연시키기.

    dispatch_time_t pushTime3s = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));

    dispatch_async(queue, ^{ NSLog(@"-> 5"); });

    dispatch_after(pushTime3s,queue, ^{ NSLog(@"-> 6"); });

    

    //---- 5-2. Serial 큐 지연시키기.

    // 일단 블럭시키고 시간이 걸리는 작업을 하자 8->6 이 예상되지만, 블럭킹처리시간으로 인해 큐에 추가되는 순서는 6->8

    // 시간지연을 주더라도 해당시간에 실행되는것이아니라 해당시간에 큐에 추가하려고 하는거다.

    // 따라서 긴시간 작업하던 7의 중간에 6이 끼어들어추가하려해서 추가됬고, 8이 그다음에 추가된거다.주의해야한다.

    typedef void (^iambloking)(void);

    iambloking blokinghehe = ^{

    NSMutableArray *array = [[NSMutableArray alloc] init];

    for ( long i = 0; i < 100000000; i++ ) {

        [array addObject:[NSNumber numberWithLong:i]];

    }};

    dispatch_async(queue, ^{

        NSLog(@"-> 7");

        dispatch_async(dispatch_queue_create("innerQ", DISPATCH_QUEUE_SERIAL),  ^{ NSLog(@"-> innerQ"); blokinghehe(); });

    }); // 오래걸리는 블럭킹을 부르자

    dispatch_async(queue, ^{ NSLog(@"-> 8"); });

    //1,2,3,4,5,7(wait),8,6


B. Concurrent Dispatch Queue 생성 
동시에 여러 태스크를 동시다발적으로 실행할수 있다. SERIAL은 한번에 한개의 태스크가 끝나야 다음 태스크로 넘어갔지만, CONCURRENT에서는 전역쓰레드의 여유에 따라서 태스크들이 순서에 상관없이 실행될수있다.  각 태스트는 각자으 ㅣ쓰레드를 생성하여 수행된다.


    
dispatch_queue_t queue = dispatch_queue_create("test"DISPATCH_QUEUE_CONCURRENT);

사용법은 SERIAL과 같다.


C. Group 생성  - 모든 태스크가 끝난는지 판단
태스크들이 모두 끝났는지를 판단하려면 태스크들을 하나의 작업으로 묶어야하는데 group 으로 가능하다. 

   //---- 6. Dispatch Group

    dispatch_queue_t queueG = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_group_t group = dispatch_group_create();


    dispatch_group_async(group, queueC, ^{

        [NSThread sleepForTimeInterval:1];

        NSLog(@"Done: %d", 1);

    });

    

    // 그룹에 추가

    dispatch_group_enter(group);

    //콜백 정의

    typedef void (^blk_t)(void (^callback)(void));

    blk_t callback = ^(void (^callback)(void)){

        NSMutableArray *array = [[NSMutableArray alloc] init];

        for ( long i = 0; i < 35000000; i++ ) {

            [array addObject:[NSNumber numberWithLong:i]];

        }

        callback();

    };

    //콜백호출

    callback(^{

        //콜백 함수로 그룹에 추가한게 끝이라고 하게함

        dispatch_group_leave(group);

    });

    //enter->leave가 끝나야 다음 태스크 실행

    dispatch_group_async(group, queueG, ^{ NSLog(@"Done' %f", 1.5); });


    dispatch_group_notify(group, queueG, ^{

        NSLog(@"All tasks are done!");

    });

    

    dispatch_group_async(group, queueG, ^{

        //[NSThread sleepForTimeInterval:2];

        NSLog(@"Done: %d", 2);

    });

    dispatch_group_async(group, queueG, ^{

       // [NSThread sleepForTimeInterval:1];

        NSLog(@"Done: %d", 3);

    });

    //https://padgom.tistory.com/entry/iOS-%EA%B8%B0%EB%B3%B8-GCDGrand-Central-Dispatch-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0





D. Barrier 특정 지점에서 장벽? 을 쳐서 그부분까지 실행하도록 하고, 다음 태스크들을 기다리게하는거
E. Semaphore 로 지정한 시간만큼 기다리게 할수 있다. 
F. Suspend 잠시 실행을 멈춘다. 다만 태스크들을 계속 추가될수있다. Resume로 복귀.
G. Dicpatch_once 로 싱글턴도 만들수 있다.  queue에 아무리 넣어도 프로그램기동후에는 한번만 실행된다.


0 comments:

댓글 쓰기