iOS 멀티스레드 개발 - GCD(二)

9361 단어
전언
이전 iOS 멀티스레드 개발 - GCD(二)에서 멀티스레드가 무엇인지, GCD와 스레드의 관계, 그리고 대기열을 만드는 방법과 간단한 사용법을 소개했습니다. 이 글에서 GCD의 고급 사용법을 중점적으로 요약하겠습니다.
dispatch 만들기queue_t 주의해야 할 문제
저희가 디스패치를 통해서...get_global_queue(long identifier, unsigned long flags);dispatch를 통해 전역 대기열 가져오기get_main_queue();홈팀 열 획득,dispatch를 통해queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);병렬 또는 직렬 큐를 만듭니다.우리가 주의해야 할 것은, 디스패치get_global_queue(long identifier, unsigned long flags);dispatch_get_main_queue();이 두 가지 방법은 모두 우리가 시스템이 이미 만든 대기열을 가져오는 것이다. 따라서 우리는 이 대기열에 대해 메모리 관리를 할 필요가 없다. 시스템은 우리가 적당한 시간에 이 대기열을 방출하는 것을 도울 것이다.
4
  • 이 책에서 저자는 우리가 디스패치 를 사용할 때queue_create(const char _Nullable label,dispatch_queue_attr_t _Nullable attr);대기열을 만들 때 이 대기열에 수동으로 메모리 관리를 해야 합니다. ARC를 사용했지만 디스패치queue_t는 하나의 대상 유형이 아니기 때문에 ARC에서 메모리 관리를 할 수 없습니다. 이럴 때dispatch를 사용해야 합니다release (queue) 가 이 대기열을 방출합니다.하지만 제 실천과 애플 공식 문서 조회에 따르면 ARC를 사용했다면 디스패치를 사용할 필요가 없습니다release 및 dispatchretain은 대기열을 관리하러 갔습니다. 사실상 ARC에서 디스패치를 사용합니다.release 및 dispatchretain이 오류를 보고합니다.*

  • GCD API 사용
    1. dispatch_queue_create
    dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr); 이 방법은 첫 번째 파라미터가 대기열의 이름일 수도 있고, 비어 있을 수도 있지만, debug에 불리합니다.두 번째 매개변수는 대기열 유형입니다. NULL이 작성된 경우 기본값은 직렬 대기열, DISPATCH 입니다.QUEUE_CONCURRENT가 병렬 큐를 생성합니다.
    //    
    dispatch_queue_t serialQueue = dispatch_queue_create("com.jiaxiang.serialQueue", DISPATCH_QUEUE_SERIAL);
    //    
    dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    

    이론적으로 우리는 이곳에서 임의의 여러 개의 대기열을 만들 수 있고, 여러 개의 직렬 대기열은 병렬로 실행할 수 있다.그러나 우리가 너무 많은 직렬 대기열을 사용했을 때 우리는 상응하는 직렬 라인을 만들 것이다. 너무 많은 라인 생성은 시스템 자원을 소모할 것이다.
    2. Main Dispatch Queue/Global Dispatch Queue
    //     ,       
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //            ,    
    dispatch_queue_t concurrentQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    

    dispatch_get_global_queue(long identifier, unsigned long flags);첫 번째 파라미터는 대기열의 우선 순위를 지정하고, 두 번째 파라미터는 flag이며, 대부분의 시간은 0을 사용한다.우선순위는 4단계로 나뉘는데 높음부터 낮음까지 높음, 기본값, 낮음, 백그라운드 순으로 나뉜다.
    #define DISPATCH_QUEUE_PRIORITY_HIGH 2
    #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
    #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
    #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
    
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_queue_t serialQueue = dispatch_queue_create("com.jiaxiang.serialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(concurrentQueue1, ^{
    //            
            NSLog(@"Concurrent Queue");
            NSLog(@"Concurrent Queue%@",[NSThread currentThread]);
            dispatch_async(mainQueue, ^{
            //           
                NSLog(@"Main Queue");
                NSLog(@"Main Queue%@",[NSThread currentThread]);
            });
        });
        NSLog(@"Done");
        NSLog(@"Done%@",[NSThread currentThread]);
    

    위에서 사용한 이 동작을 병렬 대기열에 놓고 결과를 홈 대기열에 놓고 실행할 수 있습니다.
    3.dispatch_set_target_queue
    dispatch_set_target_queue(dispatch_object_t object,dispatch_queue_t _Nullable queue);이 방법은 한 대기열의 실행 우선 순위를 다른 대기열의 우선 순위로 설정합니다.첫 번째 파라미터는 우선순위를 변경하는 대기열이고, 두 번째 파라미터는 목표 우선순위 대기열이다.첫 번째 매개변수는 시스템에서 제공하는 Main Queue 및 Global Dispatch Queue가 될 수 없습니다.
    // serialQueue        concurrentQueue2  
    dispatch_queue_t serialQueue = dispatch_queue_create("com.jiaxiang.serialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t concurrentQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_set_target_queue(serialQueue, concurrentQueue2);
    
    dispatch_queue_t serialQueue1 = dispatch_queue_create("com.jiaxiang.serialQueue1", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t serialQueue2 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t serialQueue3 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t serialQueue4 = dispatch_queue_create("com.jiaxiang.serialQueue3", DISPATCH_QUEUE_SERIAL);
        
        dispatch_async(serialQueue, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"0");
        });
        dispatch_async(serialQueue1, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"1");
        });
        dispatch_async(serialQueue2, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"2");
        });
        dispatch_async(serialQueue3, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"3");
        });
        dispatch_async(serialQueue4, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"4");
        });
    
    

    위에서 만든 다섯 개의 직렬 대기열은 실행할 때 병렬 실행되며, 매번 실행할 때마다 선후 순서가 고정되지 않습니다.그런데 저희가 디스패치를 사용하면...set_target 이후 직렬 실행이 됩니다.
    dispatch_queue_t serialQueue1 = dispatch_queue_create("com.jiaxiang.serialQueue1", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t serialQueue2 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t serialQueue3 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t serialQueue4 = dispatch_queue_create("com.jiaxiang.serialQueue3", DISPATCH_QUEUE_SERIAL);
        
        dispatch_set_target_queue(serialQueue1, serialQueue);
        dispatch_set_target_queue(serialQueue2, serialQueue);
        dispatch_set_target_queue(serialQueue3, serialQueue);
        dispatch_set_target_queue(serialQueue4, serialQueue);
        dispatch_async(serialQueue, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"0");
        });
        dispatch_async(serialQueue1, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"1");
        });
        dispatch_async(serialQueue2, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"2");
        });
        dispatch_async(serialQueue3, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"3");
        });
        dispatch_async(serialQueue4, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"4");
        });
    

    4,dispatch_after
    dispatch_after(dispatch_time_t when,dispatch_queue_t queue, dispatch_block_t block);첫 번째 파라미터는 지정된 시간 후에 임무를 지정한 대기열에 추가하고, 두 번째 파라미터는 임무를 수행하는 대기열을 지정하며, 세 번째 파라미터는 추가된 임무이다.분명히 해야 할 것은 임무가 지정된 시간 이후에 집행되는 것이 아니라 임무가 지정된 시간 후에 대기열에 추가되기 때문에 임무가 집행되는 시간은 지정된 시간 이후에 집행될 가능성이 높다는 것이다.또한 이 방법은 비동기적인 작업을 수행하기 때문에 지정한 대기열이main queue일 때도 잠금 해제를 걱정할 필요가 없습니다.
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"Hello");
        });
    NSLog(@"Done After");
    

    5,Dispatch Group
    GCD를 사용할 때, 우리는 때때로 우리가 병렬 대기열을 실행한 후에 모든 병렬 대기열의 임무를 추가해서 실행할 수 있기를 희망할 때가 있다.이때 우리는 디스패치 그룹을 사용하여 이 수요를 실현할 수 있다.
    dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
       
     dispatch_group_async(group, concurrentQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"1");
    });
        
    dispatch_group_async(group, concurrentQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"2");
    });
        
    dispatch_group_async(group, concurrentQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"3");
    });
        
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"Done");
    });
    

    6.dispatch_barrier_async
    병렬 대기열의 1, 2, 3 이 몇 가지 임무를 수행한 후에 4를 수행한 다음에 5, 6, 7을 수행하여 데이터를 쓰는 동시에 데이터를 읽고 데이터 경쟁을 피하려면 dispatchbarrier_async.
    dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueue1, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"1");
        });
    dispatch_async(concurrentQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
         NSLog(@"2");
    });
        
    dispatch_async(concurrentQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"3");
    });
        
    dispatch_barrier_async(concurrentQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"4");
    });
        
    dispatch_async(concurrentQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"5");
    });
        
    dispatch_async(concurrentQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"6");
    });
        
    dispatch_async(concurrentQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"7");
    });
    

    7.dispatch_apply
    이 함수는 지정한 횟수에 따라 지정한 작업을 대기열에 추가하고 모든 처리가 끝날 때까지 기다립니다.
    dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_apply(10, concurrentQueue1, ^(size_t index) {
        NSLog(@"%zu",index);
    });
    NSLog(@"Done");
    

    8.dispatch_suspend/dispatch_resume
    dispatch_suspend (queue), 지정한 대기열을 닫습니다. 대기열에서 실행된 작업은 영향을 주지 않으며, 실행된 작업의 실행을 정지합니다.dispatch_resume (queue), 지정한 대기열을 복원하고, 대기열에서 실행된 작업을 계속 수행합니다.
    9.Dispatch Semaphore
    신호량을 통해 대기열의 작업 실행 상황을 제어합니다.

    좋은 웹페이지 즐겨찾기