iOS多線程編程之三——GCD的應用

iOS多線程編程之三——GCD的應用

1、引言

在軟件開發中使用多線程能夠大大的提高用戶體驗度,增長工做效率。iOS系統中提供了多種分線程編程的方法,在前兩篇博客都有說起:編程

NSThread類進行多線程編程:http://my.oschina.net/u/2340880/blog/416524多線程

NSOperation進行多線程操做編程:http://my.oschina.net/u/2340880/blog/416782併發

上兩個進行多線程編程的機制都是封裝於Object-C的類與方法。這篇博客將討論的Grand Central Dispatch(GCD)機制,則是基於C語言的,相比上面兩種機制,GCD更加高效,而且線程有系統管理,會自動運用多核運算。由於這些優點,GCD是apple推薦咱們使用的多線程解決方案。app

2、GCD的調度機制

GCD機制中一個很重要的概念是調度隊列,咱們對線程的操做其實是由調度隊列完成的。咱們只須要將要執行的任務添加到合適的調度隊列中便可。框架

一、調度隊列的類型

調度隊列有三種類型:異步

(1)主隊列async

其中的任務在主線程中執行,由於其會阻塞主線程,因此這是一個串行的隊列。能夠經過dispatch_get_main_queue()方法獲得。ide

(2)全局並行隊列函數

隊列中任務的執行方式是嚴格按照先進先出的模式進行了。若是是串行的隊列,則當一個任務結束後,纔會開啓另外一個任務,若是是並行隊列,則任務的開啓順序是和添加順序一致的。系統爲iOS應用自動建立了四個全局共享的併發隊列。使用以下函數得到:性能

 

dispatch_get_global_queue(long identifier, unsigned long flags);

其中第一個參數是這個隊列的id,系統的四個全局隊列默認的優先級不一樣,這個參數可填的定義以下:

#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//後臺的全局隊列 優先級最低

這個函數的第二個參數,按照官方文檔的說法是有待將來使用,如今咱們都填0便可。

(3)自定義隊列

上面的兩種隊列都是系統爲咱們建立好的,咱們只須要獲取到他們,將任務添加便可。固然,咱們可能夠建立咱們本身的隊列,包括串行的和並行的。使用以下方法建立:

dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);

其中,第一個參數是這個隊列的名字,第二個參數決定建立的是串行的仍是並行的隊列。填寫DISPATCH_QUEUE_SERIAL或者NULL建立串行隊列,填寫DISPATCH_QUEUE_CONCURRENT建立並行隊列。

二、添加任務到隊列中

使用dispatch_sync(dispatch_queue_t queue, dispatch_block_t block)函數或者dispatch_async(dispatch_queue_t queue, dispatch_block_t block)函數來同步或者異步的執行任務,示例以下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"%@:1",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%@:2",[NSThread currentThread]);
    });
}

打印結果以下:

能夠看出第一個任務在主線程中執行,第二個在分線程中執行。

3、隊列調度機制的更多技巧

經過上面的演示,咱們已經能夠運用隊列進行多線程的執行任務,可是GCD的強大之處遠遠不止如此。

一、使用隊列組

若是有這樣三個任務,A與B是沒有關係的,他們能夠並行執行,C必須在A,B結束以後才能執行,固然,實現這樣的邏輯並不困難,使用KVO就能夠實現,可是使用隊列組處理這樣的邏輯,代碼會更加清晰簡單。

可使用dispatch_group_create()建立一個隊列組,使用以下函數將隊列添加到隊列組中:

void dispatch_group_async(dispatch_group_t group,
	dispatch_queue_t queue,
	dispatch_block_t block);

隊列組中的隊列是異步執行的,示例以下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //建立一個隊列組
    dispatch_group_t group=dispatch_group_create();
    建立一個異步隊列
    dispatch_queue_t queue=dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        for (int i=0; i<10; i++) {
            NSLog(@"%@:%d",[NSThread currentThread],i);
        }
    });
    dispatch_group_async(group, queue, ^{
        for (int i=0; i<10; i++) {
            NSLog(@"%@:%d",[NSThread currentThread],i);
        }
    });
    //阻塞線程直到隊列任務完成
    dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
    for (int i=0; i<10; i++) {
        NSLog(@"over:%d",i);
    }
}

打印出來的信息以下:

能夠看出,隊列中的任務是異步執行的,而且等待隊列組中隊列任務所有執行後才執行後面的任務。這樣的作法在實際應用中咱們不多使用,一般咱們會把後續的任務在放在異步中執行,作法以下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //建立一個隊列組
    dispatch_group_t group=dispatch_group_create();
    //建立一個隊列
    dispatch_queue_t queue=dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    //添加隊列任務到隊列組
    dispatch_group_async(group, queue, ^{
        for (int i=0; i<10; i++) {
            NSLog(@"%@:%d",[NSThread currentThread],i);
        }
    });
    dispatch_group_async(group, queue, ^{
        for (int i=0; i<10; i++) {
            NSLog(@"%@:%d",[NSThread currentThread],i);
        }
    });
    //隊列組任務執行完後執行的任務
    dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i=0; i<10; i++) {
            NSLog(@"over:%d",i);
        }
    });
    for (int i=0; i<10; i++) {
         NSLog(@"Finish:%d",i);
    }
   
}

打印信息以下:

能夠看出GCD的強大了吧,複雜的任務邏輯關係由於GCD變得十分清晰簡單。

二、循環機制

一開始咱們就提到,GCD相比NSOperation的優點在於多核心的應用,更深得挖掘出了硬件的性能。GCD在多核方面的一個明顯的特色就是循環機制。

 dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {
        NSLog(@"%@:%zu",[NSThread currentThread],i);
    });

打印結果以下:

能夠看出,程序的運行效率又會高許多。

三、消息傳遞機制

 

dispatch_source_t類型的對象能夠用來傳遞和接受某個消息,而後執行block方法,示例以下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //建立一個數據對象,DISPATCH_SOURCE_TYPE_DATA_ADD的含義表示數據變化時相加
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
    //建立接收數據變化的句柄
    dispatch_source_set_event_handler(source, ^{
        NSLog(@"%lu",dispatch_source_get_data(source));
    });
    //啓動
    dispatch_resume(source);
    //設置數據
    dispatch_source_merge_data(source, 1);
    //這步執行完以後會執行打印方法
}

 

四、發送和等待信號

GCD中還有一個重要的概念是信號量。它的用法法消息的傳遞有所相似,經過代碼來解釋:

    //建立一個信號,其中的參數爲信號的初始值
    dispatch_semaphore_t singer = dispatch_semaphore_create(0);
    //發送信號,使信號量+1
    dispatch_semaphore_signal(singer);
    //等待信號,當信號量大於0時執行後面的方法,不然等待,第二個參數爲等待的超時時長,下面設置的爲一直等待
    dispatch_semaphore_wait(singer, DISPATCH_TIME_FOREVER);
    NSLog(@"123");

經過發送信號,能夠試信號量+1,每次執行過等待信號後,信號量會-1;如此,咱們能夠很方便的控制不一樣隊列中方法的執行流程。

五、掛起和開啓任務隊列

GCD還提供了暫停與開始任務的方法,使用

void dispatch_suspend(dispatch_object_t object);

能夠將隊列或者隊列組進行暫時的掛起,使用

 

void dispatch_resume(dispatch_object_t object);

將隊列或者隊列組從新開啓。

須要注意的是,暫停隊列時,隊列中正在執行的任務並不會被中斷,會掛起未開啓的任務。
 

4、關於內存管理

GCD雖然是基於C語言封裝的框架,使用了面向對象的思想。所以,它的內存管理是須要咱們注意的,不管是ARC或者MRC,咱們都應該手動去處理這些對象。還好,GCD的內存管理思路和Object—C是兼容的,咱們使用dispatch_retain()和dispatch_release()來將引用對象的計數進行加減。這一點十分重要,切記切記。

 

疏漏之處 歡迎指正

學習使用 歡迎轉載

專一技術,熱愛生活,交流技術,也作朋友。

——琿少 QQ羣:203317592

相關文章
相關標籤/搜索