iOS多線程開發—GCD(二)

前言

在上一篇文章iOS多線程開發—GCD(二)中我介紹了什麼是多線程,GCD與線程的關係,以及如何建立隊列和簡單的使用,在這篇文章中我講重點總結一下GCD一些高級用法。編程

建立dispatch_queue_t須要注意的問題

咱們能夠經過dispatch_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);建立並行或者串行隊列。 咱們須要注意的是,dispatch_get_global_queue(long identifier, unsigned long flags);dispatch_get_main_queue();這兩種方法都是讓咱們獲取系統已經建立好的隊列,所以咱們不須要對這些隊列進行內存管理,系統會幫助咱們在合適的時間釋放這些隊列。安全

  • 在《Objective-C 高級編程》這本書中,做者寫到當咱們使用dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);去建立隊列的時候,咱們須要手動對這個隊列進行內存管理,雖然咱們使用了ARC,可是dispatch_queue_t並非一個對象類型,所以ARC沒法對它進行內存管理這時候咱們就須要使用dispatch_release(queue)去釋放這個隊列。可是根據我本身的實踐和查閱蘋果官方文檔顯示,若是咱們使用了ARC,那麼咱們是不須要使用dispatch_release和dispatch_retain去管理隊列的,事實上在ARC中使用dispatch_release和dispatch_retain會報錯。

GCD API使用

1. dispatch_queue_create

dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr); 這個方法可讓咱們建立一個隊列,第一個參數是隊列的名字,也能夠爲空,可是不利於debug。第二個參數是隊列的類型,若是寫NULL,那麼默認是串行隊列,DISPATCH_QUEUE_CONCURRENT將建立並行隊列。bash

//串行隊列
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。 優先級分爲四級,從高到低依次爲,高,默認,低,後臺app

#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。async

//將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");
    });

複製代碼

以上建立的五個串行隊列在執行的時候是並行執行的,它們每次執行的前後順序並不固定。可是當咱們使用dispatch_set_target以後它們將變成串行執行。ide

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 group來實現這個需求。post

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");
});
複製代碼
+ (void)withGroup:(dispatch_group_t)group 
        sendAsynchronousRequest:(NSURLRequest *)request 
        queue:(NSOperationQueue *)queue 
        completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
{
    if (group == NULL) {
        [self sendAsynchronousRequest:request 
                                queue:queue 
                    completionHandler:handler];
    } else {
        dispatch_group_enter(group);
        [self sendAsynchronousRequest:request 
                                queue:queue 
                    completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
            handler(response, data, error);
            dispatch_group_leave(group);
        }];
    }
}

複製代碼

6.dispatch_barrier_async

當咱們須要讓並行隊列中的1,2,3這幾個任務執行完以後,執行任務4,而後再執行任務5,6,7以此避免在在寫入數據的同同時讀取數據,形成數據競爭,這時候咱們可使用dispatch_barrier_async。

self.serialQueue = dispatch_queue_create("com.serialQueue.com", 0);
- (void)setCount:(NSUInteger)count forKey:(NSString *)key
{
    key = [key copy];
    dispatch_async(self.serialQueue, ^(){
        if (count == 0) {
            [self.counts removeObjectForKey:key];
        } else {
            self.counts[key] = @(count);
        }
    });
}

- (NSUInteger)countForKey:(NSString *)key;
{
    __block NSUInteger count;
    dispatch_sync(self.serialQueue, ^(){
        NSNumber *n = self.counts[key];
        count = [n unsignedIntegerValue];
    });
    return count;
}
複製代碼
self.concurrentQueue = dispatch_queue_creat("com.concurrentQueue.jiaxiang",DISPATCH_QUEUE_CONCURRENT);
- (void)setCount:(NSUInteger)count forKey:(NSString *)key
{
    key = [key copy];
    dispatch_barrier_async(self.isolationQueue, ^(){
        if (count == 0) {
            [self.counts removeObjectForKey:key];
        } else {
            self.counts[key] = @(count);
        }
    });
}

複製代碼
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

  • 經過信號量來控制隊列中任務的執行狀況。 dispatch_semaphore_t dispatch_semaphore_create(long value):方法接收一個long類型的參數, 返回一個dispatch_semaphore_t類型的信號量,值爲傳入的參數
  • long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout):接收一個信號和時間值,若信號的信號量爲0,則會阻塞當前線程,直到信號量大於0或者通過輸入的時間值;若信號量大於0,則會使信號量減1並返回,程序繼續住下執行
  • long dispatch_semaphore_signal(dispatch_semaphore_t dsema):使信號量加1並返回
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
   dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

   __block int j = 0;
   dispatch_async(queue, ^{
        j = 100;
        dispatch_semaphore_signal(semaphore);
   });

   dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
   NSLog(@"finish j = %zd", j);
複製代碼
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

for (int i = 0; i < 100; i++) {
     dispatch_async(queue, ^{
          // 至關於加鎖
          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
          NSLog(@"i = %zd semaphore = %@", i, semaphore);
          // 至關於解鎖
          dispatch_semaphore_signal(semaphore);
      });
}

複製代碼

10.dispatch_once

dispathc_once函數能夠確保某個block在應用程序執行的過程當中只被處理一次,並且它是線程安全的。

+ (Manager *)sharedInstance {
    static Manager *sharedManagerInstance = nil;
    static dispatch_once_t once;

    dispatch_once($once, ^{
        sharedManagerInstance = [[Manager alloc] init];
    });

    return sharedManagerInstance;
}
複製代碼
相關文章
相關標籤/搜索