iOS多線程系列(3)

來源:互聯網
上載者:User

在2011的WWDC上,蘋果推出了GCD,從此多線程增加了一種新的方法。GCD要求運行在iOS4.0版本以上或者OS X10.6版本以上。GCD是Grand Central Dispatch的縮寫,是一組用於實現並發編程的C介面。GCD是基於Objective-C的Block的特性開發的,基本的商務邏輯和NSOperation很像。都是添加一個任務到一個隊列,由系統來負責線程的產生和調度。因為直接使用Block,所以使用起來很是方便,降低了多線程開發的門檻。

還是先看一下代碼,和多線程系列(1)裡面同一個例子,用GCD實現如下:

- (void)viewDidLoad{    [super viewDidLoad];       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        [self downloadImage:IMAGE_URL];    });}
GCD的調用介面非常簡單,就是將任務提交到Queue裡面。

dispatch_async函數是非同步非阻塞的,調用後會立刻返回,工作由系統線上程池中分配線程去執行。有非同步當然也有同步的,dispatch_sync就是同步的阻塞的API,會一直到添加的任務完成才會返回。

GCD實現多線程確實很簡單,不需要瞭解多線程中的很多細節,而且效率也高。不過disaptch_queue有一些特殊的地方,實際使用中需要瞭解的多一些。dispatch_queue有串列運行和並行運行兩種,顧名思義,串列運行就是任務順序執行,完成一個然後執行下一個,每次只有一個任務在運行;並行運行就是各個任務可以同時運行,同時有多少任務可以並行是根據系統當時的負載決定的,這個開發人員不用關心。

系統提供了3中類型的dispatch queue:

1. main queue

這實際上就是主線程的隊列,所以很明顯,這是一個串列的queue,所有加入main queue的任務都會發動主線程運行,所以加入任務時需要注意不要加入長時間啟動並執行任務。

2. Global queue

我們實際開發中最常用的隊列,是並發隊列。並且有high、default、low三個優先順序(每個優先順序都對應一個獨立的queue)。通過dispatch_get_global_queue這個API可以獲得queue。

3. 自訂queue

dispatch queue是可以自己建立的,通過dispatch_queue_create這個API來建立,dispatch_queue_create(const char *label, dispatch_queue_attr attr)這個API的第一個參數是queue的名字,要求不能重複,所以很多時候和java一樣,推薦用倒寫的網域名稱,第二個參數是建立的queue的類型。這裡要指出,在iOS4.3之前,只能建立串列的queue,參數就是傳遞DISPATCH_QUEUE_SERIAL,iOS4.3之後可以建立並行的queue了,參數是DISPATCH_QUEUE_CONCURRENT。

看到create就會牽涉到記憶體的管理問題,GCD的記憶體管理同樣是用引用計數的方式,不過並不納入iOS的記憶體管理,所以是需要開發人員手動管理的(無論是不是ARC)。

由於有著不同類型的隊列,dispatch_async也可以嵌套使用,還是以同樣的例子,我們也可以這樣寫:

- (void)viewDidLoad{    [super viewDidLoad];    __block UIImage *_image;       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:IMAGE_URL]];        _image = [[UIImage alloc] initWithData:data];        dispatch_async(dispatch_get_main_queue(), ^{            self.imageView.image = _image;        });    });}
這樣就在一段代碼裡面實現了所有的功能,包括後台下載,下載之後重新整理UI,而且簡單清晰。

還有一些常用的API介紹如下:

dispatch_get_current_queue()擷取當前隊列

dispatch_queue_get_label()擷取隊列的名字,如果隊列沒有名字,返回NULL

dispatch_set_target_queue()設定給定對象的目標隊列

dispatch_main()會阻塞主線程等待主隊列main queue中的Block執行結束。


有時我們會遇到運行一系列的任務,當任務全部結束後運行另一個特殊的任務這種情境。如果我們用dispatch_sync方法來串列運行所有的任務可以確定啟動並執行先後順序,但效率就會大大降低;但dispatch_async是非同步非阻塞的,所以代碼如下寫是沒用的,不能保證結束所有任務後那個特殊任務的已耗用時間點。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    for(id obj in array)        dispatch_async(queue, ^{            [self doWork:obj];        });    [self doneWork];

針對這種情況,GCD提供了dispatch group,可以將一組任務集合在一起,等待這組任務完成後再繼續,上面的情境,代碼應該寫成下面的樣子:

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_group_t group = dispatch_group_create();    for(id obj in array)        dispatch_group_async(group, queue, ^{            [self doWork:obj];        });    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);    dispatch_release(group);    [self doneWork];

方法很簡單,就是將並發的任務用dispatch_group_async非同步添加到一個Group和全域隊列中,dispatch_group_wait會等待這些工作完成後在返回。這樣就實現了任務的順序運行,不過dispatch_group_wait是會阻塞線程的,所以如果是主線程,這個API是不能調用的,那麼我們該怎麼辦呢?

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_group_t group = dispatch_group_create();    for(id obj in array)        dispatch_group_async(group, queue, ^{            [self doWork:obj];        });    dispatch_group_notify(group, queue, ^{        [self doneWork];    });    dispatch_release(group);
答案還是很簡單,換一個API,使用dispatch_group_notify這個方法即可。


有的時候我們要同步執行對數組元素的逐個操作,GCD提供了一個簡單的dispatch_apply方法:

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_apply([array count], queue, ^(size_t index){        [self doWork:obj:[array objectAtIndex:index]];    });    [self doneWork];

在使用dispatch_async方法提交並行的任務時,是無法確定任務的執行順序的,但有時我們確實需要某些工作在某個工作完成之後執行,那麼可以使用Dispatch Barrier介面來實現。

    dispatch_async(queue, block1);    dispatch_async(queue, block2);    dispatch_barrier_async(queue, block3);    dispatch_async(queue, block4);    dispatch_async(queue, block5);
dispatch_barrier_async是非同步,調用後立刻返回。這樣的寫法會保證block1和block2並存執行完成後才會執行block3,完成後再會並行運行block4和block5。

請注意,這裡的queue是一個並行隊列,而且是自訂的那種。


作為蘋果推出的多線程的神器,GCD的內容當然遠遠不止這些。不過通過介紹的最最常用的這些,我們已經可以管中窺豹了。GCD針對各種不同的需求考慮的很全面,並給出了相關的解決方案。開發人員使用GCD應該說是很容易的,所以真正需要關心的就變成了任務怎麼劃分,怎麼運行,是串列還是並行等等。

附上蘋果的Grand Central Dispatch(GCD)Reference文檔,需要深入瞭解的請參考。







聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.