日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

多线程编程4 - GCD

發布時間:2024/9/30 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 多线程编程4 - GCD 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、簡介

在iOS所有實現多線程的方案中,GCD應該是最有魅力的,因為GCD本身是蘋果公司為多核的并行運算提出的解決方案。GCD在工作時會自動利用更多的處理器核心,以充分利用更強大的機器。GCD是Grand Central Dispatch的簡稱,它是基于C語言的。如果使用GCD,完全由系統管理線程,我們不需要編寫線程代碼。只需定義想要執行的任務,然后添加到適當的調度隊列(dispatch queue)。GCD會負責創建線程和調度你的任務,系統直接提供線程管理


二、調度隊列(dispath queue)

1.GCD的一個重要概念是隊列,它的核心理念:將長期運行的任務拆分成多個工作單元,并將這些單元添加到dispath queue中,系統會為我們管理這些dispath queue,為我們在多個線程上執行工作單元,我們不需要直接啟動和管理后臺線程。

2.系統提供了許多預定義的dispath queue,包括可以保證始終在主線程上執行工作的dispath queue。也可以創建自己的dispath queue,而且可以創建任意多個。GCD的dispath queue嚴格遵循FIFO(先進先出)原則,添加到dispath queue的工作單元將始終按照加入dispath queue的順序啟動。

3.dispatch queue按先進先出的順序,串行或并發地執行任務

1> serial dispatch queue一次只能執行一個任務, 當前任務完成才開始出列并啟動下一個任務

2> concurrent dispatch queue則盡可能多地啟動任務并發執行


三、創建和管理dispatch queue

1.獲得全局并發Dispatch Queue (concurrent dispatch queue)

1> 并發dispatch queue可以同時并行地執行多個任務,不過并發queue仍然按先進先出的順序來啟動任務。并發queue會在之前的任務完成之前就出列下一個任務并開始執行。并發queue同時執行的任務數量會根據應用和系統動態變化,各種因素包括:可用核數量、其它進程正在執行的工作數量、其它串行dispatch queue中優先任務的數量等.

2> 系統給每個應用提供三個并發dispatch queue,整個應用內全局共享,三個queue的區別是優先級。你不需要顯式地創建這些queue,使用dispatch_get_global_queue函數來獲取這三個queue:

[java]?view plaincopy
  • //?獲取默認優先級的全局并發dispatch?queue??
  • dispatch_queue_t??queue?=?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0);??
  • 第一個參數用于指定優先級,分 別使用DISPATCH_QUEUE_PRIORITY_HIGH和DISPATCH_QUEUE_PRIORITY_LOW兩個常 來獲取高和低優先級的兩個queue;第二個參數目前未使用到,默認0即可

    3> 雖然dispatch queue是引用計數的對象,但你不需要retain和release全局并發queue。因為這些queue對應用是全局的,retain和release調用會被忽略。你也不需要存儲這三個queue的引用,每次都直接調用dispatch_get_global_queue獲得queue就行了。


    2.創建串行Dispatch Queue (serial dispatch queue)

    1> 應用的任務需要按特定順序執行時,就需要使用串行Dispatch Queue,串行queue每次只能執行一個任務。你可以使用串行queue來替代鎖,保護共享資源 或可變的數據結構。和鎖不一樣的是,串行queue確保任務按可預測的順序執行。而且只要你異步地提交任務到串行queue,就永遠不會產生死鎖

    2> 你必須顯式地創建和管理所有你使用的串行queue,應用可以創建任意數量的串行queue,但不要為了同時執行更多任務而創建更多的串行queue。如果你需要并發地執行大量任務,應該把任務提交到全局并發queue

    3> 利用dispatch_queue_create函數創建串行queue,兩個參數分別是queue名和一組queue屬性

    [java]?view plaincopy
  • dispatch_queue_t?queue;??
  • queue?=?dispatch_queue_create("cn.itcast.queue",?NULL);??

  • 3.運行時獲得公共Queue
    GCD提供了函數讓應用訪問幾個公共dispatch queue:

    1> 使用dispatch_get_current_queue函數作為調試用途,或者測試當前queue的標識。在block對象中調用這個函數會返回block提交到的queue(這個時候queue應該正在執行中)。在block對象之外調用這個函數會返回應用的默認并發queue。
    2> 使用dispatch_get_main_queue函數獲得應用主線程關聯的串行dispatch queue
    3> 使用dispatch_get_global_queue來獲得共享的并發queue


    4.Dispatch Queue的內存管理

    1> Dispatch Queue和其它dispatch對象(還有dispatch source)都是引用計數的數據類型。當你創建一個串行dispatch queue時,初始引用計數為 1,你可以使用dispatch_retain和dispatch_release函數來增加和減少引用計數。當引用計數到達 0 時,系統會異步地銷毀這個queue

    2> 對dispatch對象(如dispatch queue)retain和release 是很重要的,確保它們被使用時能夠保留在內存中。和OC對象一樣,通用的規則是如果使用一個傳遞過來的queue,你應該在使用前retain,使用完之后release

    3> 你不需要retain或release全局dispatch queue,包括全局并發dispatch queue和main dispatch queue

    4> 即使你實現的是自動垃圾收集的應用,也需要retain和release創建的dispatch queue和其它dispatch對象。GCD 不支持垃圾收集模型來回收內存


    四、添加任務到queue

    要執行一個任務,你需要將它添加到一個適當的dispatch queue,你可以單個或按組來添加,也可以同步或異步地執行一個任務,也。一旦進入到queue,queue會負責盡快地執行你的任務。一般可以用一個block來封裝任務內容。

    1.添加單個任務到queue

    1> 異步添加任務

    你可以異步或同步地添加一個任務到Queue,盡可能地使用dispatch_async或dispatch_async_f函數異步地調度任務。因為添加任務到Queue中時,無法確定這些代碼什么時候能夠執行。因此異步地添加block或函數,可以讓你立即調度這些代碼的執行,然后調用線程可以繼續去做其它事情。特別是應用主線程一定要異步地 dispatch 任務,這樣才能及時地響應用戶事件

    2> 同步添加任務

    少數時候你可能希望同步地調度任務,以避免競爭條件或其它同步錯誤。 使用dispatch_sync和dispatch_sync_f函數同步地添加任務到Queue,這兩個函數會阻塞當前調用線程,直到相應任務完成執行。注意:絕對不要在任務中調用 dispatch_sync或dispatch_sync_f函數,并同步調度新任務到當前正在執行的 queue。對于串行queue這一點特別重要,因為這樣做肯定會導致死鎖;而并發queue也應該避免這樣做。

    3> 代碼演示

    [java]?view plaincopy
  • //?調用前,查看下當前線程??
  • NSLog(@"當前調用線程:%@",?[NSThread?currentThread]);??
  • ??
  • //?創建一個串行queue??
  • dispatch_queue_t?queue?=?dispatch_queue_create("cn.itcast.queue",?NULL);??
  • ??
  • dispatch_async(queue,?^{??
  • ????NSLog(@"開啟了一個異步任務,當前線程:%@",?[NSThread?currentThread]);??
  • });??
  • ??
  • dispatch_sync(queue,?^{??
  • ????NSLog(@"開啟了一個同步任務,當前線程:%@",?[NSThread?currentThread]);??
  • });??
  • //?銷毀隊列??
  • dispatch_release(queue);??
  • 打印信息:

    [java]?view plaincopy
  • 2013-02-03?09:03:37.348?thread[6491:c07]?當前調用線程:<NSThread:?0x714fa80>{name?=?(null),?num?=?1}??
  • 2013-02-03?09:03:37.349?thread[6491:1e03]?開啟了一個異步任務,當前線程:<NSThread:?0x74520a0>{name?=?(null),?num?=?3}??
  • 2013-02-03?09:03:37.350?thread[6491:c07]?開啟了一個同步任務,當前線程:<NSThread:?0x714fa80>{name?=?(null),?num?=?1}??

  • 2.并發地執行循環迭代

    如果你使用循環執行固定次數的迭代, 并發dispatch queue可能會提高性能。

    例如下面的for循環:

    [java]?view plaincopy
  • int?i;??
  • int?count?=?10;??
  • for?(i?=?0;?i?<?count;?i++)?{??
  • ???printf("%d??",i);??
  • }??
  • 1> 如果每次迭代執行的任務與其它迭代獨立無關,而且循環迭代執行順序也無關緊要的話,你可以調用dispatch_apply或dispatch_apply_f函數來替換循環。這兩個函數為每次循環迭代將指定的block或函數提交到queue。當dispatch到并發 queue時,就有可能同時執行多個循環迭代。用dispatch_apply或dispatch_apply_f時你可以指定串行或并發 queue。并發queue允許同時執行多個循環迭代,而串行queue就沒太大必要使用了。

    下面代碼使用dispatch_apply替換了for循環,你傳遞的block必須包含一個size_t類型的參數,用來標識當前循環迭代。第一次迭代這個參數值為0,最后一次值為count - 1

    [java]?view plaincopy
  • //?獲得全局并發queue??
  • dispatch_queue_t?queue?=?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0);??
  • size_t?count?=?10;??
  • dispatch_apply(count,?queue,?^(size_t?i)?{??
  • ????printf("%zd?",?i);??
  • });??
  • //?銷毀隊列??
  • dispatch_release(queue);??
  • 打印信息:

    [java]?view plaincopy
  • 1?2?0?3?4?5?6?7?8?9???
  • 可以看出,這些迭代是并發執行的

    和普通for循環一樣,dispatch_apply和dispatch_apply_f函數也是在所有迭代完成之后才會返回,因此這兩個函數會阻塞當前線程,主線程中調用這兩個函數必須小心,可能會阻止事件處理循環并無法響應用戶事件。所以如果循環代碼需要一定的時間執行,可以考慮在另一個線程中調用這兩個函數。如果你傳遞的參數是串行queue,而且正是執行當前代碼的queue,就會產生死鎖


    3.在主線程中執行任務

    1> GCD提供一個特殊的dispatch queue,可以在應用的主線程中執行任務。只要應用主線程設置了run loop(由CFRunLoopRef類型或NSRunLoop對象管理),就會自動創建這個queue,并且最后會自動銷毀。非Cocoa應用如果不顯式地設置run loop, 就必須顯式地調用dispatch_main函數來顯式地激活這個dispatch queue,否則雖然你可以添加任務到queue,但任務永遠不會被執行。

    2> 調用dispatch_get_main_queue函數獲得應用主線程的dispatch queue,添加到這個queue的任務由主線程串行化執行

    3> 代碼實現,比如異步下載圖片后,回到主線程顯示圖片

    [java]?view plaincopy
  • //?異步下載圖片??
  • dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{??
  • ????NSURL?*url?=?[NSURL?URLWithString:@"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg"];??
  • ????UIImage?*image?=?[UIImage?imageWithData:[NSData?dataWithContentsOfURL:url]];??
  • ??????
  • ????//?回到主線程顯示圖片??
  • ????dispatch_async(dispatch_get_main_queue(),?^{??
  • ????????self.imageView.image?=?image;??
  • ????});??
  • });??

  • 4.任務中使用Objective-C對象

    GCD支持Cocoa內存管理機制,因此可以在提交到queue的block中自由地使用Objective-C對象。每個dispatch queue維護自己的autorelease pool確保釋放autorelease對象,但是queue不保證這些對象實際釋放的時間。如果應用消耗大量內存,并且創建大量autorelease對象,你需要創建自己的autorelease pool,用來及時地釋放不再使用的對象。


    五、暫停和繼續queue

    我們可以使用dispatch_suspend函數暫停一個queue以阻止它執行block對象;使用dispatch_resume函數繼續dispatch queue。調用dispatch_suspend會增加queue的引用計數,調用dispatch_resume則減少queue的引用計數。當引用計數大于0時,queue就保持掛起狀態。因此你必須對應地調用suspend和resume函數。掛起和繼續是異步的,而且只在執行block之間(比如在執行一個新的block之前或之后)生效。掛起一個queue不會導致正在執行的block停止。


    六、Dispatch Group的使用

    假設有這樣一個需求:從網絡上下載兩張不同的圖片,然后顯示到不同的UIImageView上去,一般可以這樣實現

    [java]?view plaincopy
  • //?根據url獲取UIImage??
  • -?(UIImage?*)imageWithURLString:(NSString?*)urlString?{??
  • ????NSURL?*url?=?[NSURL?URLWithString:urlString];??
  • ????NSData?*data?=?[NSData?dataWithContentsOfURL:url];??
  • ????return?[UIImage?imageWithData:data];??
  • }??
  • ??
  • -?(void)downloadImages?{??
  • ????//?異步下載圖片??
  • ????dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{??
  • ????????//?下載第一張圖片??
  • ????????NSString?*url1?=?@"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg";??
  • ????????UIImage?*image1?=?[self?imageWithURLString:url1];??
  • ??????????
  • ????????//?下載第二張圖片??
  • ????????NSString?*url2?=?@"http://hiphotos.baidu.com/lvpics/pic/item/3a86813d1fa41768bba16746.jpg";??
  • ????????UIImage?*image2?=?[self?imageWithURLString:url2];??
  • ??????????
  • ????????//?回到主線程顯示圖片??
  • ????????dispatch_async(dispatch_get_main_queue(),?^{??
  • ????????????self.imageView1.image?=?image1;??
  • ??????????????
  • ????????????self.imageView2.image?=?image2;??
  • ????????});??
  • ????});??
  • }??
  • 雖然這種方 案可以解決問題,但其實兩張圖片的下載過程并不需要按順序執行,并發執行它們可以提高執行速度。有個注意點就是必須等兩張圖片都下載完畢后才能回到主線程顯示圖片。Dispatch Group能夠在這種情況下幫我們提升性能。下面先看看Dispatch Group的用處:

    我們可以使用dispatch_group_async函數將多個任務關聯到一個Dispatch Group和相應的queue中,group會并發地同時執行這些任務。而且Dispatch Group可以用來阻塞一個線程, 直到group關聯的所有的任務完成執行。有時候你必須等待任務完成的結果,然后才能繼續后面的處理。

    下面用Dispatch Group優化上面的代碼:

    [java]?view plaincopy
  • //?根據url獲取UIImage??
  • -?(UIImage?*)imageWithURLString:(NSString?*)urlString?{??
  • ????NSURL?*url?=?[NSURL?URLWithString:urlString];??
  • ????NSData?*data?=?[NSData?dataWithContentsOfURL:url];??
  • ????//?這里并沒有自動釋放UIImage對象??
  • ????return?[[UIImage?alloc]?initWithData:data];??
  • }??
  • ??
  • -?(void)downloadImages?{??
  • ????dispatch_queue_t?queue?=?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0);??
  • ??????
  • ????//?異步下載圖片??
  • ????dispatch_async(queue,?^{??
  • ????????//?創建一個組??
  • ????????dispatch_group_t?group?=?dispatch_group_create();??
  • ??????????
  • ????????__block?UIImage?*image1?=?nil;??
  • ????????__block?UIImage?*image2?=?nil;??
  • ??????????
  • ????????//?關聯一個任務到group??
  • ????????dispatch_group_async(group,?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{??
  • ????????????//?下載第一張圖片??
  • ????????????NSString?*url1?=?@"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg";??
  • ????????????image1?=?[self?imageWithURLString:url1];??
  • ????????});??
  • ??????????
  • ????????//?關聯一個任務到group??
  • ????????dispatch_group_async(group,?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{??
  • ????????????//?下載第一張圖片??
  • ????????????NSString?*url2?=?@"http://hiphotos.baidu.com/lvpics/pic/item/3a86813d1fa41768bba16746.jpg";??
  • ????????????image2?=?[self?imageWithURLString:url2];??
  • ????????});??
  • ??????????
  • ????????//?等待組中的任務執行完畢,回到主線程執行block回調??
  • ????????dispatch_group_notify(group,?dispatch_get_main_queue(),?^{??
  • ????????????self.imageView1.image?=?image1;??
  • ????????????self.imageView2.image?=?image2;??
  • ??????????????
  • ????????????//?千萬不要在異步線程中自動釋放UIImage,因為當異步線程結束,異步線程的自動釋放池也會被銷毀,那么UIImage也會被銷毀??
  • ??????????????
  • ????????????//?在這里釋放圖片資源??
  • ????????????[image1?release];??
  • ????????????[image2?release];??
  • ????????});??
  • ??????????
  • ????????//?釋放group??
  • ????????dispatch_release(group);??
  • ????});??
  • }??
  • dispatch_group_notify函數用來指定一個額外的block,該block將在group中所有任務完成后執行

    總結

    以上是生活随笔為你收集整理的多线程编程4 - GCD的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。