你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS 多線程(四)GCD

iOS 多線程(四)GCD

編輯:IOS開發綜合

GCD:Grand Central Dispatch(GCD) 是異步執行任務的技術之一。一般將應用程序中記述的線程管理用的代碼在系統級中實現。開發者只需要定義想執行的任務並追加到適當的Dispatch Queue中,GCD就能生成必要的線程並計劃執行任務。由於線程管理是作為系統的一部分來實現的,因此可以統一管理,也可執行任務,這樣就比以前的線程更有效率。

1 Dispatch Queue

“Dispatch Queue”是什麼,如其名稱所示,是執行處理的等待隊列。Dispatch Queue 按照追加的順序執行處理。先進先出FIFO,first-in-first-out。

隊列類型:

1 Serial Dispatch Queue 等待現在執行處理結束

2 ConcurrentDispatch Queue 不等待現在執行處理結束

比較兩種Dispatch Queue。

 

dispatch_sync(queue,block1);
dispatch_sync(queue,block2);
dispatch_sync(queue,block3);
dispatch_sync(queue,block4);
dispatch_sync(queue,block5);
dispatch_sync(queue,block6);
當queue為serial Dispatch Queue,會先執行block1,當block1執行完畢後,接著執行block2,當block2執行完畢後,接著執行block3,如此重復。同時執行的處理數只能有一個。

 

block1 -> block2-> block3-> block4-> block5-> block6

當queue為Concurrent Dispatch Queue時,首先執行block1,不管block1是否結束,都開始執行後面的block2,不管block2是否結束,都開始執行block3,如此重復循環。(同時執行的處理數量取決於當前系統的狀態。)

 

線程1 線程2 線程3 block1 block2 block3 block4 block6 block5

 

創建方式

第一種方式:dispatch_queue_create

 

// 創建串行隊列    DISPATCH_QUEUE_SERIAL = NULL
    dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_SERIAL);

 

 

// 1.創建一個並發隊列
    dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_CONCURRENT);

雖然Serial Dispatch Queue 和Current Dispatch Queue將受到限制,但是使用dispatch_queue_create函數可生成人意多個Dispatch Queue。

當生成多個Serial Dispatch Queue時,雖然在一個Serial Dispatch Queue中同時只能執行一個追加處理,但多個Serial Dispatch Queue之間可以並行執行。

通過dispatch_queue_create 函數生成的Dispatch Queue,在使用結束後需要釋放。

 

//釋放
dispatch_release(queue);

第二種方式:獲取系統標准提供的Dispatch Queue。

Main Dispatch Queue,是在主線程中執行的Dispatch Queue,屬於Serial Dispatch Queue。

 

// 1.獲得主隊列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
追加到Main Dispatch Queue的處理在主線程的RunLoop中執行。因此要將用戶界面的更新等一些必須在主線程中執行的處理追加到Main Dispatch Queue使用。與NSObject類的persormSelectorOnMainThread實例方法相同。

 

GlobalDispatch Queue是ConCurrent Dispatch Queue。所有應用程序都可以使用,沒有必要通過dispatch_queue_create函數逐個生成Concurrent Dispatch Queue。

 

// 1.獲得全局的並發隊列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
第一個參數表示優先級。Global Dispatch Queue有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

2 執行任務方式

GCD有兩種方法執行任務。

1 同步的方式執行任務

dispatch_sync(dispatch_queue_t queue,dispatch_block_t block);

2 異步的方式執行任務

 

dispatch_async(dispatch_queue_t queue,dispatch_block_t block);

同步和異步的區別

同步:只能在當前線程中執行任務,不具備開啟新線程的能力

異步:可以在新的線程中執行任務,具備開啟新線程的能力


串行隊列 同步函數

 

/**
 * 串行隊列 + 同步函數:不會開啟新的線程,在當前線程執行任務。任務是串行的,執行完一個任務,再執行下一個任務
 */
-(void)syncSerial{
    NSLog(@"syncSerial ----- begin");
    //創建串行隊列 serial
    dispatch_queue_t serialQueue = dispatch_queue_create("com.vn.serial", NULL);
    dispatch_sync(serialQueue, ^{
        NSLog(@"1...sync....%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"2...sync....%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"3...sync....%@",[NSThread currentThread]);
    });
    NSLog(@"syncSerial ----- end");
}
打印:

 

 

2016-10-19 15:36:43.810 GCD基本使用[9712:1396171] syncSerial ----- begin
2016-10-19 15:36:43.810 GCD基本使用[9712:1396171] 1...sync....{number = 1, name = main}
2016-10-19 15:36:43.811 GCD基本使用[9712:1396171] 2...sync....{number = 1, name = main}
2016-10-19 15:36:43.811 GCD基本使用[9712:1396171] 3...sync....{number = 1, name = main}
2016-10-19 15:36:43.811 GCD基本使用[9712:1396171] syncSerial ----- end

 

串行隊列 異步函數

 

/**
 * 串行隊列 + 異步函數 :會開啟新的線程,但是任務是串行的,執行完一個任務,再執行下一個任務
 */
-(void)asyncSerial{
    NSLog(@"asyncSerial ----- begin");
    //創建串行隊列 serial
    dispatch_queue_t serialQueue = dispatch_queue_create("com.vn.serial", NULL);
    dispatch_async(serialQueue, ^{
        NSLog(@"1...async....%@",[NSThread currentThread]);
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"2...async....%@",[NSThread currentThread]);
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"3...async....%@",[NSThread currentThread]);
    });
    NSLog(@"asyncSerial ----- end");
}

 

打印:

2016-10-19 15:38:55.354 GCD基本使用[9730:1397289] asyncSerial ----- begin
2016-10-19 15:38:55.354 GCD基本使用[9730:1397289] asyncSerial ----- end
2016-10-19 15:38:55.354 GCD基本使用[9730:1397758] 1...async....{number = 3, name = (null)}
2016-10-19 15:38:55.355 GCD基本使用[9730:1397758] 2...async....{number = 3, name = (null)}
2016-10-19 15:38:55.355 GCD基本使用[9730:1397758] 3...async....{number = 3, name = (null)}

主隊列 同步函數

/**
 * 主隊列 + 同步函數: 死鎖,不應該這樣使用
 */
- (void)syncMain{
    NSLog(@"syncMain ----- begin");
    // 1.獲得主隊列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_sync(mainQueue, ^{
        NSLog(@"1--syncMain---%@", [NSThread currentThread]);
    });
    NSLog(@"syncMain ----- end");
}
只輸出下面數據後就會卡死。
syncMain ----- begin

主隊列 異步函數

 

/**
 * 主隊列 + 異步函數: 在主線程中執行任務,不會阻塞主線程。任務是串行的,執行完一個任務,再執行下一個任務
 */
- (void)asyncMain{
    NSLog(@"asyncMain ----- begin");
    // 1.獲得主隊列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(mainQueue, ^{
        NSLog(@"1--asyncMain---%@", [NSThread currentThread]);
    });
    dispatch_async(mainQueue, ^{
        NSLog(@"2--asyncMain---%@", [NSThread currentThread]);
    });
    dispatch_async(mainQueue, ^{
        NSLog(@"3--asyncMain---%@", [NSThread currentThread]);
    });
    NSLog(@"asyncMain ----- end");
}
打印:

 

 

2016-10-19 15:39:56.747 GCD基本使用[9750:1398368] asyncMain ----- begin
2016-10-19 15:39:56.748 GCD基本使用[9750:1398368] asyncMain ----- end
2016-10-19 15:39:56.748 GCD基本使用[9750:1398368] 1--asyncMain---{number = 1, name = main}
2016-10-19 15:39:56.753 GCD基本使用[9750:1398368] 2--asyncMain---{number = 1, name = main}
2016-10-19 15:39:56.753 GCD基本使用[9750:1398368] 3--asyncMain---{number = 1, name = main}

並行隊列 同步函數

/**
 * 並發隊列 + 同步函數:不會開啟新的線程,由於不開啟新線程,需要等待執行完,所以任務執行完一個,再執行下一個。
 */
- (void)syncConcurrent{
    // 1.獲得全局的並發隊列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.將任務加入隊列
    dispatch_sync(queue, ^{
        NSLog(@"1--syncConcurrent---%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2--syncConcurrent---%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3--syncConcurrent---%@", [NSThread currentThread]);
    });
    NSLog(@"syncConcurrent--------end");
}
打印:

 

 

2016-10-19 15:52:30.536 GCD基本使用[9790:1404534] syncConcurrent--------begin
2016-10-19 15:52:30.536 GCD基本使用[9790:1404534] 1--syncConcurrent---{number = 1, name = main}
2016-10-19 15:52:30.536 GCD基本使用[9790:1404534] 2--syncConcurrent---{number = 1, name = main}
2016-10-19 15:52:30.537 GCD基本使用[9790:1404534] 3--syncConcurrent---{number = 1, name = main}
2016-10-19 15:52:30.537 GCD基本使用[9790:1404534] syncConcurrent--------end

並行隊列 異步函數

/**
 * 並發隊列 + 異步函數:可以同時開啟多條線程
 */
- (void)asyncConcurrent{
    NSLog(@"asyncConcurrent--------begin");
    // 1.獲得全局的並發隊列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.將任務加入隊列
    dispatch_async(queue, ^{
        NSLog(@"1--asyncConcurrent---%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2--asyncConcurrent---%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3--asyncConcurrent---%@", [NSThread currentThread]);
    });
    NSLog(@"asyncConcurrent--------end");
}
打印:
2016-10-19 15:55:34.637 GCD基本使用[9820:1406397] asyncConcurrent--------begin
2016-10-19 15:55:34.637 GCD基本使用[9820:1406397] asyncConcurrent--------end
2016-10-19 15:55:34.637 GCD基本使用[9820:1406608] 1--asyncConcurrent---{number = 3, name = (null)}
2016-10-19 15:55:34.637 GCD基本使用[9820:1406613] 2--asyncConcurrent---{number = 4, name = (null)}
2016-10-19 15:55:34.637 GCD基本使用[9820:1406614] 3--asyncConcurrent---{number = 5, name = (null)}

3 線程間通信

dispatch_async(dispatch_get_main_queue(), ^{//});

- (void)threadCommunication{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 圖片的網絡路徑
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        // 加載圖片
        NSData *data = [NSData dataWithContentsOfURL:url];
        // 生成圖片
        UIImage *image = [UIImage imageWithData:data];
        // 回到主線程
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });
}

4 延時

// 延時追加
- (void)delay{
    NSLog(@"delay-----begin");
    //延時1s
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
    dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"after------%@",[NSThread currentThread]);
    });
    NSLog(@"delay-----end");
}

打印:
2016-10-19 16:35:45.680 GCD基本使用[9932:1427078] delay-----begin
2016-10-19 16:35:45.680 GCD基本使用[9932:1427078] delay-----end
2016-10-19 16:35:46.752 GCD基本使用[9932:1427078] after------{number = 1, name = main}
dispatch_after 並不是延時指定時間後執行處理,而是延時指定時間後追加任務到Dispatch Queue。

其中dispatch_time(DISPATCH_TIME_NOW,1ull*NSEC_PER_SEC);表示從現在開始1s後時間的值。

DISPATCH_TIME_NOW 表示當前的時間。
第二個參數單位為納秒。

#define NSEC_PER_SEC 1000000000ull  //每秒有多少納秒
#define NSEC_PER_MSEC 1000000ull    //每毫秒有多少納秒
#define USEC_PER_SEC 1000000ull     //每秒有多少微秒
#define NSEC_PER_USEC 1000ull       //每微秒有多少納秒
延時1s寫法:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
dispatch_time_t time1 = dispatch_time(DISPATCH_TIME_NOW, 1000ull*NSEC_PER_MSEC);
dispatch_time_t time2 = dispatch_time(DISPATCH_TIME_NOW, 1000ull*USEC_PER_SEC);

5Dispatch Group

開發中會有這種需求,執行多個任務,只有這幾個任務都執行完成後才執行最終任務。這種情況可以使用Dispatch Queue。

 

- (void)group{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 創建一個隊列組
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{
        for (int i=0; i<5; i++) {
            NSLog(@"任務1-------%d",i);
        }
    });
    dispatch_group_async(group, queue, ^{
        for (int i=0; i<5; i++) {
            NSLog(@"任務2-------%d",i);
        }
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"end.....");
    });
}
打印:
2016-10-19 18:05:56.138 GCD基本使用[10052:1471672] 任務1-------0
2016-10-19 18:05:56.138 GCD基本使用[10052:1471674] 任務2-------0
2016-10-19 18:05:56.139 GCD基本使用[10052:1471674] 任務2-------1
2016-10-19 18:05:56.139 GCD基本使用[10052:1471672] 任務1-------1
2016-10-19 18:05:56.139 GCD基本使用[10052:1471674] 任務2-------2
2016-10-19 18:05:56.139 GCD基本使用[10052:1471672] 任務1-------2
2016-10-19 18:05:56.139 GCD基本使用[10052:1471674] 任務2-------3
2016-10-19 18:05:56.139 GCD基本使用[10052:1471672] 任務1-------3
2016-10-19 18:05:56.139 GCD基本使用[10052:1471674] 任務2-------4
2016-10-19 18:05:56.139 GCD基本使用[10052:1471672] 任務1-------4
2016-10-19 18:05:56.139 GCD基本使用[10052:1471672] end....

6 dispatch_barrier_async

在訪問數據庫或文件時,寫入處理確實不可與其他的寫入處理以及其他包含讀寫的處理並行執行。但是讀取處理只是和讀取處理並行執行就不會發生問題。

為了高效的進行訪問,讀取處理追加到Concurrent Dispatch Queue中,寫入處理在一個讀取都沒有的情況下,追加到Serial Dispatch Queue中可。利用Dispatch Group和dispatch_set_target_queue也可以實現,但是會很復雜。

GCD提供了簡便方法——dispatch_barrier_async函數。

 

- (void)barrier{
    dispatch_queue_t queue = dispatch_queue_create("abcd", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4-----%@", [NSThread currentThread]);
    });
}
打印:
2016-10-19 19:18:30.739 GCD基本使用[10096:1490892] ----2-----{number = 4, name = (null)}
2016-10-19 19:18:30.739 GCD基本使用[10096:1490890] ----1-----{number = 3, name = (null)}
2016-10-19 19:18:30.740 GCD基本使用[10096:1490890] ----barrier-----{number = 3, name = (null)}
2016-10-19 19:18:30.740 GCD基本使用[10096:1490890] ----3-----{number = 3, name = (null)}
2016-10-19 19:18:30.740 GCD基本使用[10096:1490892] ----4-----{number = 4, name = (null)}
在前兩個任務執行完後,才會追加處理到該queue中。然後當該處理執行完畢後,開始追加其他處理。

7 dispatch_apply

- (void)apply{
    NSLog(@"apply------begin");
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //該函數按指定的次數 將block追加到Dispatch Queue中。並等待全部處理執行結束。
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"apply------%zu",index);
    });
    NSLog(@"apply------end");
}
打印:
2016-10-19 19:45:19.186 GCD基本使用[10114:1500525] apply------begin
2016-10-19 19:45:19.187 GCD基本使用[10114:1500525] apply------1
2016-10-19 19:45:19.187 GCD基本使用[10114:1500833] apply------0
2016-10-19 19:45:19.187 GCD基本使用[10114:1500837] apply------2
2016-10-19 19:45:19.187 GCD基本使用[10114:1500838] apply------3
2016-10-19 19:45:19.187 GCD基本使用[10114:1500525] apply------4
2016-10-19 19:45:19.187 GCD基本使用[10114:1500833] apply------5
2016-10-19 19:45:19.187 GCD基本使用[10114:1500837] apply------6
2016-10-19 19:45:19.187 GCD基本使用[10114:1500838] apply------7
2016-10-19 19:45:19.188 GCD基本使用[10114:1500525] apply------8
2016-10-19 19:45:19.188 GCD基本使用[10114:1500833] apply------9
2016-10-19 19:45:19.188 GCD基本使用[10114:1500525] apply------end
可以使用該函數遍歷NSArray對象,

dispatch_apply([array count],queue,^(size_t index){});

由於dispatch_apply函數與dispatch_sync相同,會等待處理執行結束,因此推薦在dispatch_async函數中異步執行dispatch_apply函數。

 

- (void)apply1{
    NSLog(@"apply1------begin");
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        dispatch_apply(5, queue, ^(size_t index) {
            NSLog(@"apply------%zu",index);
        });
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"done");
        });
    });
    NSLog(@"apply1------end");
}
打印:
2016-10-19 20:26:16.245 GCD基本使用[10143:1518627] apply1------begin
2016-10-19 20:26:16.246 GCD基本使用[10143:1518627] apply1------end
2016-10-19 20:26:16.246 GCD基本使用[10143:1518924] apply------0
2016-10-19 20:26:16.246 GCD基本使用[10143:1518928] apply------1
2016-10-19 20:26:16.246 GCD基本使用[10143:1518929] apply------2
2016-10-19 20:26:16.246 GCD基本使用[10143:1518930] apply------3
2016-10-19 20:26:16.246 GCD基本使用[10143:1518924] apply------4
2016-10-19 20:26:16.251 GCD基本使用[10143:1518627] done

8 dispatch_once

dispatch_once 函數保證應用程序中只執行一次。

 

- (void)once{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"------run");
    });
}

 

 

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved