iOS GCD學習記錄

weixin_34161083發表於2017-08-31

1,對於建立全域性使用的序列或者並行佇列,都應該用strong修飾,例如

  • @property (nonatomic,strong) dispatch_queue_t dispatch_serial

這是因為在ios6.0之前,oc是沒辦法自動管理gcd佇列的,所以在iOS6.0之前要使用assign。但是iOS6.0之後,蘋果在oc裡面加入了自動管理gcd的功能。

2,有關內容的學習

這裡首先要記住
序列佇列是:DISPATCH_QUEUE_SERIAL
並行佇列是:DISPATCH_QUEUE_CONCURRENT
同步執行:dispatch_sync(<這裡寫是序列還是並行>, {<程式碼要完成的事>})
非同步執行:dispatch_async(<這裡寫是序列還是並行>, {<程式碼要完成的事>})

注:只要是非同步執行都會建立新的執行緒,同步執行不會建立新的執行緒。序列佇列肯定是上一個任務執行完才會執行下一個任務,並行佇列理論上可以說是同時執行,任務完成的順序和cpu的分配有關

首選宣告兩個全域性使用的佇列,一個序列佇列,一個並行佇列

@property (nonatomic,strong) dispatch_queue_t dispatch_serial;/**<序列佇列*/
@property (nonatomic,strong) dispatch_queue_t dispatch_concurrent;/**<並行佇列*/

在viewDidLoad裡面建立這兩個佇列

- (void)viewDidLoad {
    [super viewDidLoad];
    //建立兩個佇列
    _dispatch_serial = dispatch_queue_create("dispatch_serial", DISPATCH_QUEUE_SERIAL);
    _dispatch_concurrent = dispatch_queue_create("dispatch_concurrent", DISPATCH_QUEUE_CONCURRENT);
    
    [self serialAndSynchronize];
    [self serialAndASynchronize];
    [self concurrentAndSynchronize];
    [self concurrentAndASynchronize];
    
}

下面就是對不同情況下簡單的使用

序列同步佇列

//序列同步佇列
-(void)serialAndSynchronize{
    dispatch_sync(_dispatch_serial, ^{
        NSLog(@"1---------%@",[NSThread currentThread]);
    });
    dispatch_sync(_dispatch_serial, ^{
        NSLog(@"2---------- %@",[NSThread currentThread]);
    });
    dispatch_sync(_dispatch_serial, ^{
        NSLog(@"3---------- %@",[NSThread currentThread]);
    });
    NSLog(@"4------------ %@",[NSThread currentThread]);
}

列印的資料

2017-08-31 14:23:52.340 SYGCDStudy[8903:207494] 1---------<NSThread: 0x60000006c700>{number = 1, name = main}
2017-08-31 14:23:52.340 SYGCDStudy[8903:207494] 2---------- <NSThread: 0x60000006c700>{number = 1, name = main}
2017-08-31 14:23:52.340 SYGCDStudy[8903:207494] 3---------- <NSThread: 0x60000006c700>{number = 1, name = main}
2017-08-31 14:23:52.341 SYGCDStudy[8903:207494] 4------------ <NSThread: 0x60000006c700>{number = 1, name = main}

這裡可以看到。序列同步佇列實際上都是在主執行緒上面執行的,而且沒有建立新的執行緒,並行都是按照順序執行的,必須等前一個執行結束,才會執行下一個

序列非同步佇列

//序列非同步佇列
-(void)serialAndASynchronize{
    dispatch_async(_dispatch_serial, ^{
        NSLog(@"1------------ %@",[NSThread currentThread]);
    });
    dispatch_async(_dispatch_serial, ^{
        NSLog(@"2------------ %@",[NSThread currentThread]);
    });
    dispatch_async(_dispatch_serial, ^{
        NSLog(@"3------------ %@",[NSThread currentThread]);
    });
    NSLog(@"4------------ %@",[NSThread currentThread]);
}

列印的資料

2017-08-31 14:25:02.175 SYGCDStudy[8925:209538] 1------------ <NSThread: 0x600000267000>{number = 3, name = (null)}
2017-08-31 14:25:02.175 SYGCDStudy[8925:209484] 4------------ <NSThread: 0x60800007ca80>{number = 1, name = main}
2017-08-31 14:25:02.175 SYGCDStudy[8925:209538] 2------------ <NSThread: 0x600000267000>{number = 3, name = (null)}
2017-08-31 14:25:02.175 SYGCDStudy[8925:209538] 3------------ <NSThread: 0x600000267000>{number = 3, name = (null)}

先列印了4,然後順序在子執行緒中列印1,2,3。說明非同步執行具有開闢新執行緒的能力,並且序列佇列必須等到前一個任務執行完才能開始執行下一個任務,同時,非同步執行會使內部函式率先返回,不會與正在執行的外部函式發生死鎖。

並行同步佇列

//並行同步佇列
-(void)concurrentAndSynchronize{
    dispatch_sync(_dispatch_concurrent, ^{
        NSLog(@"1------------ %@",[NSThread currentThread]);
    });
    dispatch_sync(_dispatch_concurrent, ^{
       NSLog(@"2------------ %@",[NSThread currentThread]);
    });
    dispatch_sync(_dispatch_concurrent, ^{
        NSLog(@"3------------ %@",[NSThread currentThread]);
    });
    NSLog(@"4------------ %@",[NSThread currentThread]);
}

列印的資料

2017-08-31 14:30:10.572 SYGCDStudy[8953:213767] 1------------ <NSThread: 0x60800007c180>{number = 1, name = main}
2017-08-31 14:30:10.572 SYGCDStudy[8953:213767] 2------------ <NSThread: 0x60800007c180>{number = 1, name = main}
2017-08-31 14:30:10.573 SYGCDStudy[8953:213767] 3------------ <NSThread: 0x60800007c180>{number = 1, name = main}
2017-08-31 14:30:10.573 SYGCDStudy[8953:213767] 4------------ <NSThread: 0x60800007c180>{number = 1, name = main}

未開啟新的執行緒執行任務,並且Block函式執行完成後dispatch函式才會返回,才能繼續向下執行,所以我們看到的結果是順序列印的。

並行非同步佇列

//並行非同步佇列
-(void)concurrentAndASynchronize{
    dispatch_async(_dispatch_concurrent, ^{
       NSLog(@"1------------ %@",[NSThread currentThread]);
    });
    dispatch_async(_dispatch_concurrent, ^{
       NSLog(@"2------------ %@",[NSThread currentThread]);
    });
    dispatch_async(_dispatch_concurrent, ^{
        NSLog(@"3------------ %@",[NSThread currentThread]);
    });
    NSLog(@"4------------ %@",[NSThread currentThread]);
}

列印的資料

2017-08-31 14:34:08.966 SYGCDStudy[9008:220555] 3------------ <NSThread: 0x608000079380>{number = 5, name = (null)}
2017-08-31 14:34:08.966 SYGCDStudy[9008:220478] 4------------ <NSThread: 0x60000006da80>{number = 1, name = main}
2017-08-31 14:34:08.966 SYGCDStudy[9008:220557] 2------------ <NSThread: 0x600000077340>{number = 4, name = (null)}
2017-08-31 14:34:08.966 SYGCDStudy[9008:220554] 1------------ <NSThread: 0x608000079040>{number = 3, name = (null)}

開闢了多個執行緒,觸發任務的時機是順序的,但是我們看到完成任務的時間卻是隨機的,這取決於CPU對於不同執行緒的排程分配,但是,執行緒不是無條件無限開闢的,當任務量足夠大時,執行緒是會重複利用的。

3,其他一些常用的gcd方法

dispatch_once ,只執行一次的方法

//只執行一次
- (IBAction)carryOutOne:(id)sender {
    static dispatch_once_t oneDispatch;
    dispatch_once(&oneDispatch, ^{
        NSLog(@"這個方法只會執行一次");
    });
}

無論調起幾次這個方法,列印只會進行一次

dispatch_apply ,重複執行, 如果任務佇列是並行佇列,重複執行的任務會併發執行,如果任務佇列為序列佇列,則任務會順序執行,需要注意的是,該函式為同步函式,要防止執行緒阻塞和死鎖

//重複執行
- (IBAction)recurButton:(id)sender {
    dispatch_apply(5, _dispatch_serial, ^(size_t i) {
        NSLog(@"重複執行的次數。    %ld",i);
    });
}

dispatch_after,延時執行,其中引數dispatch_time_t代表延時時長,dispatch_queue_t代表使用哪個佇列。如果佇列是主佇列,那麼任務在主執行緒執行,如果佇列為全域性佇列或者自己建立的佇列,那麼任務在子執行緒執行

//延時執行
- (IBAction)afterButton:(id)sender {
    NSLog(@"延時三秒執行主執行緒--- 開始時間。%@",[NSDate date]);
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
    dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"延時三秒執行主執行緒。%@",[NSDate date]);
    });
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延時五秒執行主執行緒。%@",[NSDate date]);
    });
}

dispatch_group_async ,組佇列 。dispatch_group_notify,接收組佇列完成後執行的佇列。當加入到佇列組中的所有任務執行完成之後,會呼叫dispatch_group_notify函式通知任務全部完成

//分組完成
- (IBAction)groupButton:(id)sender {
    dispatch_group_t groupDispatch = dispatch_group_create();
    __block NSInteger index_1 = 0;
    __block NSInteger index_2 = 0;
    dispatch_group_async(groupDispatch, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
         dispatch_apply(10000, _dispatch_serial, ^(size_t i) {
             index_1 = index_1 + i;
        });
    });
    dispatch_group_async(groupDispatch, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_apply(2000, _dispatch_serial, ^(size_t i) {
            index_2 = index_2 + i;
        });
    });
    dispatch_group_notify(groupDispatch, dispatch_get_main_queue(), ^{
        NSLog(@"這時候的 %ld,%ld",index_1,index_2);
    });
}

該文章練習的demo地址:demo