GCD學習總結

GCD

GCD的全稱是Grand Central Dispatch,字面意思就是“極好的中樞調度器”,它能通過隊列和任務的形式實現多線程編程。使得編程人員不需要編寫線程代碼。

一、 dispatch queue

GCD的使用就是開發者只需要將想要執行的任務放入隊列中就能實現相應的多線程操作。那么隊列是什么呢? 就像它的名字一樣,就是執行處理任務的隊列。即 dispatch queue。

dispatch queue有兩種類型,分別為

  • 串行隊列 (serial dispatch queue)
  • 并發隊列 (concurrent dispatch queue)

串行隊列和并發隊列的區別,放入串行隊列中的任務,需要等待執行中的任務處理結束才能進行下一個任務。而放入并發隊列的任務,與之相反,不需要等待當前執行中的任務處理結束就可以同時進行其它的任務。

隊列的創建方式

1 通過 dispatch_queue_create 創建隊列

通過系統提供的 dispatch_queue_create API可以創建dispatch queue。

// 創建串行隊列
    dispatch_queue_t serialQueue = dispatch_queue_create("come.example.gcd", NULL);

// 創建并發隊列
  dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);    

dispatch_queue_create 有2個參數,第一個參數是用來指定dispatch queue的名稱,一般用程序的bundle id 來命名。第二個參數的作用是用來指定隊列的類型的作用。當指定為null時,是創建串行隊列,當指定為DISPATCH_QUEUE_CONCURRENT 時為并發隊列。

2 利用系統自帶的隊列

在不想自己去創建隊列的情況下,還可以通過系統提供的dispatch queue。分別為main dispatch queue 和 global dispatch queue。

main dispatch queue 就和它的名字一樣,是在主線程執行操作的隊列。因為主線程只有一個,所以它必然是串行隊列。而global dispatch queue 則是所有線程都能操作的并發隊列。

// 主隊列
 dispatch_queue_t mainQueue = dispatch_get_main_queue();

// 全局隊列
 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

對于全局對列 global dispatch queue的創建函數它有2個參數,第一個參數是指定隊列的執行優先級,而第二個參數 官方的介紹是目前還沒有用到,默認傳0就可以了。

global queue的執行優先級有:

  • DISPATCH_QUEUE_PRIORITY_HIGH 2

  • DISPATCH_QUEUE_PRIORITY_DEFAULT 0

  • DISPATCH_QUEUE_PRIORITY_LOW (-2)

  • DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

dispatch_sync和dispatch_async

dispatch_sync和dispatch_async 分別為同步操作函數和異步操作函數。它們都有2個參數,分別為執行的操作和操作添加到的隊列。同步函數是必須要隊列里的操作執行完成,不然就會一直等待。而異步函數則不需要做任何等待。

這里將列舉不同的隊列執行sync和async函數時的情況:

1 在串行隊列 同步執行任務 (這里用 sleepForTimeInterval 來模擬耗時操作,下同)

//同步 串行隊列
- (void)test2 {
    dispatch_queue_t serialQueue = dispatch_queue_create("come.example.gcd", NULL);
    NSLog(@"begin");
    dispatch_sync(serialQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

執行結果:

2017-01-14 23:41:59.772 GCD_test[11960:1198750] begin
2017-01-14 23:41:59.772 GCD_test[11960:1198750] task1 <NSThread: 0x7fd46ac05270>{number = 1, name = main}
2017-01-14 23:42:01.774 GCD_test[11960:1198750] task2 <NSThread: 0x7fd46ac05270>{number = 1, name = main}
2017-01-14 23:42:03.775 GCD_test[11960:1198750] task3 <NSThread: 0x7fd46ac05270>{number = 1, name = main}
2017-01-14 23:42:05.775 GCD_test[11960:1198750] end

結論:加入到隊列中的任務按添加順序執行,所有的操作執行完后才打印end.沒有創建新的線程。

2 在串行隊列 異步執行任務

//異步 串行隊列
- (void)test3 {
    dispatch_queue_t serialQueue = dispatch_queue_create("come.example.gcd", NULL);
    NSLog(@"begin");
    dispatch_async(serialQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

執行結果:

2017-01-14 23:49:14.422 GCD_test[11977:1202271] begin
2017-01-14 23:49:14.423 GCD_test[11977:1202271] end
2017-01-14 23:49:14.423 GCD_test[11977:1202430] task1 <NSThread: 0x7fdf09f240e0>{number = 2, name = (null)}
2017-01-14 23:49:16.428 GCD_test[11977:1202430] task2 <NSThread: 0x7fdf09f240e0>{number = 2, name = (null)}
2017-01-14 23:49:18.431 GCD_test[11977:1202430] task3 <NSThread: 0x7fdf09f240e0>{number = 2, name = (null)}

結論:加入到隊列中的任務按順序執行,不需要等待任務執行完成就能進行end的打印,創建了一條新的線程。

3 在并發隊列 同步執行任務

//同步 并發隊列
- (void)test4 {
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

執行結果:

2017-01-14 23:57:36.889 GCD_test[12010:1206188] begin
2017-01-14 23:57:36.889 GCD_test[12010:1206188] task1 <NSThread: 0x7fb5bb701ab0>{number = 1, name = main}
2017-01-14 23:57:38.890 GCD_test[12010:1206188] task2 <NSThread: 0x7fb5bb701ab0>{number = 1, name = main}
2017-01-14 23:57:40.890 GCD_test[12010:1206188] task3 <NSThread: 0x7fb5bb701ab0>{number = 1, name = main}
2017-01-14 23:57:42.892 GCD_test[12010:1206188] end

結論: 加入到隊列中的任務按順序執行,所有的操作執行完后才打印end,沒有創建新的線程。

4 在并發隊列 異步執行任務

//異步 并發隊列
- (void)test5 {
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    dispatch_async(concurrentQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

執行結果:

2017-01-15 00:02:25.811 GCD_test[12027:1208849] begin
2017-01-15 00:02:25.812 GCD_test[12027:1208849] end
2017-01-15 00:02:25.812 GCD_test[12027:1208900] task2 <NSThread: 0x7fc771d1cac0>{number = 3, name = (null)}
2017-01-15 00:02:25.812 GCD_test[12027:1208909] task1 <NSThread: 0x7fc771d2be60>{number = 2, name = (null)}
2017-01-15 00:02:25.812 GCD_test[12027:1208914] task3 <NSThread: 0x7fc771f04870>{number = 4, name = (null)}

結論:加入到隊列中的任務不按順序執行,不需要等待任務執行完成就能進行end的打印,創建了多條新的線程。

5 在主隊列 同步執行任務 (主隊列其實算一種特殊的串行隊列,它的所有任務都在主線程執行,所以單獨拿出來做討論)

//同步 主隊列
- (void)test6 {
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    NSLog(@"begin");
    dispatch_sync(mainQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(mainQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(mainQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

執行結果:

2017-01-15 00:07:03.129 GCD_test[12045:1210976] begin

結論:這里比較特殊 只打印了函數開始的begin。只是因為主隊列中的新添加的操作需要等待該函數的執行完成,而由于是同步執行,該函數又需要等待操作執行完才能繼續往下執行。所以這樣互相等待就形成了一種死鎖的情況。

6 在主隊列 異步執行任務

//異步 主隊列
- (void)test7 {
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    NSLog(@"begin");
    dispatch_async(mainQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(mainQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(mainQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

執行結果:

2017-01-15 00:11:03.326 GCD_test[12058:1213150] begin
2017-01-15 00:11:03.326 GCD_test[12058:1213150] end
2017-01-15 00:11:03.330 GCD_test[12058:1213150] task1 <NSThread: 0x7fb92be00720>{number = 1, name = main}
2017-01-15 00:11:05.332 GCD_test[12058:1213150] task2 <NSThread: 0x7fb92be00720>{number = 1, name = main}
2017-01-15 00:11:07.333 GCD_test[12058:1213150] task3 <NSThread: 0x7fb92be00720>{number = 1, name = main}

結論:加入到隊列中的任務按順序執行,不需要等待任務執行完成就能進行end的打印,在主線程執行。

出現死鎖的情況

在上面的例子中,當在主線程中將主隊列加入到sync函數中時,會產生死鎖的情況。初次之外還有別的情況會出現死鎖的情況。如下:

//死鎖的情況
- (void)test8 {
    dispatch_queue_t serialQueue = dispatch_queue_create("come.example.gcd", NULL);
    NSLog(@"begin");
    dispatch_async(serialQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        dispatch_sync(serialQueue, ^{
            NSLog(@"task2 sync");
        });
    });
    NSLog(@"end");
}

執行結果:

2017-01-15 18:27:28.518 GCD_test[12233:1223867] begin
2017-01-15 18:27:28.518 GCD_test[12233:1223867] end
2017-01-15 18:27:28.518 GCD_test[12233:1223983] task1 <NSThread: 0x7ff503d037f0>{number = 2, name = (null)}

結論: task1的任務能正常執行,但是task2的任務由于是sync函數添加的,它需要等待task2的任務,該函數才能再往下執行,而該函數又是serialQueue,其中的任務需要一個接一個執行。task1需要task2執行完,task2需要等待task1執行完,所以就產生了死鎖的情況。

二、 GCD的常見API

dispatch_after

dispatch_after是能讓任務在指定的時間之后才開始執行。等價于在指定的時間之后,再將任務添加到隊列中去。

dispatch_after(dispatch_time_t when,
    dispatch_queue_t queue,
    dispatch_block_t block);

三個參數分別為

1 when:指定的時間類型。

參數when是一種叫 dispatch_time_t 的類型。它可以通過dispatch_time()函數創建

//代表從現在開始3秒后的時間
 dispatch_time_t time= dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
 

2 queue: 任務添加到的隊列

3 block: 將要執行的任務

調用dispatch_after如下:

- (void)test9 {
    NSLog(@"begin");
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_time_t time= dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
    dispatch_after(time, mainQueue, ^{
        NSLog(@"task");
    });
    NSLog(@"end");
}

執行結果:

2017-01-15 18:49:07.876 GCD_test[12257:1230032] begin
2017-01-15 18:49:07.877 GCD_test[12257:1230032] end
2017-01-15 18:49:11.159 GCD_test[12257:1230032] task

結論: dispatch_after函數不需要等待block執行完成,類似與異步函數。在指定的時候才開始執行添加的任務。

dispatch_group

通常會有這樣的需求,在追加到隊列的任務全部結束之后,要進行某些操作。如果是在串行隊列中的話,只要在最后再向隊列中添加想要進行的操作就行了。但是要是并發隊列或者多個不同的隊列時,實現起來就比較復雜了。這時就可以借助 dispatch_group 來實現了。如下所示: 將3個任務添加到 并發隊列 異步執行。在任務執行完成后打印done。

//dispatch_group
- (void)test10 {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    NSLog(@"begin");
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_notify(group, mainQueue, ^{
        NSLog(@"done");
    });
    NSLog(@"end");
}

執行結果如下: 三個task的打印順序不一定,done一定是在其它task執行完成后打印。end的打印不需要等待 done 執行完成。

2017-01-15 21:05:13.576 GCD_test[12306:1246083] begin
2017-01-15 21:05:13.577 GCD_test[12306:1246083] end
2017-01-15 21:05:13.577 GCD_test[12306:1246147] task2 <NSThread: 0x7ffe9370e750>{number = 2, name = (null)}
2017-01-15 21:05:13.577 GCD_test[12306:1246153] task1 <NSThread: 0x7ffe93410820>{number = 3, name = (null)}
2017-01-15 21:05:13.577 GCD_test[12306:1246157] task3 <NSThread: 0x7ffe934176b0>{number = 4, name = (null)}
2017-01-15 21:05:15.610 GCD_test[12306:1246083] 所有任務結束

結論: dispatch_group_async 與 dispatch_async類似,無需等待block完成就能往下執行。區別是將 queue和block 添加到了 group中。dispatch_group_notify能監聽到group內的任務全部執行完成。而且不需要等待dispatch_group_notify 內的block執行完成就能繼續往下執行。

除了用 dispatch_group_notify 函數監聽任務執行完成外,還可以通dispatch_group_wait來監聽任務是否執行結束。如下:

//dispatch_group_wait
- (void)test11 {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"done");
    NSLog(@"end");
}

執行結果: 三個task的打印順序不一定,done一定是在其它task執行完成后打印。end的打印也是在done之后

2017-01-15 21:20:34.275 GCD_test[12326:1250587] begin
2017-01-15 21:20:34.276 GCD_test[12326:1250654] task3 <NSThread: 0x7fed1be01f30>{number = 3, name = (null)}
2017-01-15 21:20:34.276 GCD_test[12326:1250640] task2 <NSThread: 0x7fed1bc3d7c0>{number = 2, name = (null)}
2017-01-15 21:20:34.276 GCD_test[12326:1250650] task1 <NSThread: 0x7fed1bf04580>{number = 4, name = (null)}
2017-01-15 21:20:36.279 GCD_test[12326:1250587] done
2017-01-15 21:20:36.279 GCD_test[12326:1250587] end

結論: dispatch_group_wait函數會阻塞住當前線程,指到在指定的時間內group內的任務執行完成,才能繼續往下走。在這個實例中 dispatch_group_wait 的第二個參數等待時間是 DISPATCH_TIME_FOREVER。意味著永久等待直到任務全部執行結束為止。而且 dispatch_group_wait 有一個 long 類型的返回值。如果將該參數改為DISPATCH_TIME_NOW 即不等待又會是什么樣的結果。

- (void)test12 {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);
    NSLog(@"%ld",result);
    NSLog(@"done");
    NSLog(@"end");
}

執行結果:

2017-01-15 21:28:55.279 GCD_test[12339:1253834] begin
2017-01-15 21:28:55.279 GCD_test[12339:1253834] 49
2017-01-15 21:28:55.279 GCD_test[12339:1253834] done
2017-01-15 21:28:55.280 GCD_test[12339:1253834] end
2017-01-15 21:28:55.279 GCD_test[12339:1253907] task1 <NSThread: 0x7fb6d9e1aac0>{number = 2, name = (null)}
2017-01-15 21:28:55.279 GCD_test[12339:1253918] task3 <NSThread: 0x7fb6d9f03950>{number = 4, name = (null)}
2017-01-15 21:28:55.280 GCD_test[12339:1253895] task2 <NSThread: 0x7fb6d9c14b00>{number = 3, name = (null)}

結論: 如果在等待時間內group內的操作全部執行完成了,則 dispatch_group_wait 的返回值為0.

dispatch_barrier_async

在訪問數據庫或者文件時,一般要勁量避免出現數據錯亂的問題。通常如果只進行讀的操作時,可以多條線程同時執行,但在進行寫的操作時不能有其它的讀的操作和寫的操作,不然就會出現數據錯亂的問題。也就是說在進行寫的操作時 必須是在其它操作結束的時候。其實通過dispatch group可以實現。但是通過 dispatch_barrier_async 能更簡單的實現。

// dispatch_barrier_async
- (void)test13 {
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"write %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read4 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read5 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read6 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

執行結果:

2017-01-15 22:01:25.858 GCD_test[12380:1264205] begin
2017-01-15 22:01:25.858 GCD_test[12380:1264205] end
2017-01-15 22:01:25.859 GCD_test[12380:1264265] read1 <NSThread: 0x7f9672605620>{number = 2, name = (null)}
2017-01-15 22:01:25.859 GCD_test[12380:1264270] read3 <NSThread: 0x7f9672603c60>{number = 4, name = (null)}
2017-01-15 22:01:25.859 GCD_test[12380:1264257] read2 <NSThread: 0x7f9672532050>{number = 3, name = (null)}
2017-01-15 22:01:27.865 GCD_test[12380:1264270] write <NSThread: 0x7f9672603c60>{number = 4, name = (null)}
2017-01-15 22:01:29.869 GCD_test[12380:1264270] read4 <NSThread: 0x7f9672603c60>{number = 4, name = (null)}
2017-01-15 22:01:29.869 GCD_test[12380:1264257] read5 <NSThread: 0x7f9672532050>{number = 3, name = (null)}
2017-01-15 22:01:29.869 GCD_test[12380:1264265] read6 <NSThread: 0x7f9672605620>{number = 2, name = (null)}

結論: 寫操作write 一定在 前3個read操作完成的時候,而后面3個read操作一定在 寫操作之后。

就像有 dispatch_barrier_async 函數,必然會有dispatch_barrier_sync函數。該函數就如dispatch_sync函數一樣 等待 dispatch_barrier_sync的block執行完成后才會繼續往下執行。

dispatch_semaphore

當多線程進行數據的更新處理時,會容易產生數據不一致的錯誤。雖然可以借助dispatch_barrier_async來處理,但還有一種更細的粒度的處理函數dispatch_semaphore。比如以下的多線程更新數據操作:

- (void)test14 {
    NSMutableArray *tempArray = [NSMutableArray array];
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    for (int i = 0; i < 10; i++) {
        dispatch_async(concurrentQueue, ^{
            NSLog(@"obj %d",i);
            [tempArray addObject:@(i)];
        });
    }
    NSLog(@"end");
}

由于該函數是多線程異步更新 數組的操作,很容易錯誤導致程序崩潰。

dispatch_semaphore是一種持有計數的信號。這種信號類似于一種指示燈,當在某種數值時,允許操作。當在其它數值時,則不允許操作。dispatch_semaphore_t 便是這種計數信號,它可以通過dispatch_semaphore_create()函數創建,其中的參數為創建后 dispatch_semaphore_t 的計數值是多少。與之配合使用的2個函數是dispatch_semaphore_wait 和 dispatch_semaphore_signal。

1 dispatch_semaphore_wait()函數有2個參數,第一個是 dispatch_semaphore_t 類型的計數值,第二個是 dispatch_time_t 的等待時間。當 dispatch_semaphore_t 的值大于等于1時才能繼續往下執行。然后 dispatch_semaphore_t 的值減 1.

2 dispatch_semaphore_signal()函數的參數 為dispatch_semaphore_t 類型的計數值,它能使計數值加1.

之前的函數利用dispatch_semaphore后如下:

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    NSMutableArray *tempArray = [NSMutableArray array];
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    for (int i = 0; i < 10; i++) {
        dispatch_async(concurrentQueue, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"obj %d",i);
            [tempArray addObject:@(i)];
            dispatch_semaphore_signal(semaphore);
        });
    }
    NSLog(@"end");
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容

  • 章節目錄 什么是GCD? 如何在多條路徑中執行CPU命令列? 即使多線程存在很多問題(如數據競爭、死鎖、線程過多消...
    DrunkenMouse閱讀 876評論 1 13
  • 背景 擔心了兩周的我終于輪到去醫院做胃鏡檢查了!去的時候我都想好了最壞的可能(胃癌),之前在網上查的癥狀都很相似。...
    Dely閱讀 9,262評論 21 42
  • 談到iOS多線程,一般都會談到四種方式:pthread、NSThread、GCD和NSOperation。其中,蘋...
    攻城獅GG閱讀 284評論 0 3
  • 以前總在說一些有的沒的,關于青春、關于失去,那些已不屬于現在的你我,你總是說過去未來都可以,只有今天不行,而我只有...
    純_美妞閱讀 268評論 0 0
  • 都說產品是親媽,運營是奶媽/養母,一個生一個養,但都把孩子當作自己的,自然免不了常有撕逼發生。 在公司經常會出現這...
    奔跑的大橘子閱讀 637評論 3 25