你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> GCD使用以及多線程開發

GCD使用以及多線程開發

編輯:IOS開發綜合

對於iOS多線程開發,我們時刻處於學習之中,在看書中,看文檔中,項目開發中,都可以去提高自己。最近剛看完了《Objective-C高級編程 iOS與OS X多線程和內存管理》這本書後,對多線程有了更為深入的理解,故在此做一個總結與記錄。這本書是iOS開發者必讀的書之一,寫得很不錯。書的封面如下,故也稱獅子書:

\

(1)多線程會遇到的問題

\

多線程會出現什麼問題呢?當多個線程對同一個數據進行修改的時候,會造成數據不一致的情況;當多個線程互相等待會造成死鎖;過多的線程並發會大量消耗內存。所以多線程出現bug會嚴重影響App的功能和性能。

 

(2)多線程的作用

我們為什麼一定要用多線程呢?只要有一個主線程不就可以了麼。

\

 

從圖中可以看到,如果我們有一個很耗時的操作放到主線程中執行,那麼會嚴重阻塞主線程的執行,導致主界面不能及時響應用戶的操作,出現卡死狀態。當創建多線程之後,我們可以把耗時操作放到其他線程中去執行,比如網絡操作,圖片的上傳下載等,當執行成功後再回到主線程更新界面即可。所以,使用多線程是必要的。

 

(3)GCD中的隊列——Dispatch Queue

\

Dispatch Queue就是GCD中的調度隊列,我們只要把任務添加到隊列中去,線程就會按照順序取出然後去執行,按照先進先出FIFO的原則。

 

 

(4)Dispatch Queue的種類

GCD中存在兩種Dispatch Queue,一種是等待現在執行中處理的Serial Dispatch Queue(串行隊列);另一種是不等待現在執行中處理的Concurrent Dispatch Queue(並發隊列).

Serial Dispatch Queue:等待現在執行中處理結束;

Concurrent Dispatch Queue:不等待現在執行中處理結束,使用多個線程同時執行多個處理。

\

 

Serial Dispatch Queue為什麼一定要等待處理結束才去執行下一個處理呢?因為Serial Dispatch Queue只有一個線程,一個線程同一時間當然只能執行一個任務,所以後續任務必須要進行等待。

Concurrent Dispatch Queue由於可以創建多個線程,所以只要按順序取出任務即可,把取出的任務放到不同的線程去執行,任務之間是否執行結束沒有必然關系,所以不需要等待前一個處理結束,看起來就像是多個任務同時在執行一樣,其實也的確是在同時執行。當然這個並發數量系統會有限制,我們代碼也可以設置最大並發數。

看了以上的解釋,就知道Serial Dispatch Queue、Concurrent Dispatch Queue和線程的關系了,關系如下:

\

 

 

(5)多個Serial Dispatch Queue實現並發,以及遇到的問題

當生成多個Serial Dispatch Queue時,各個Serial Dispatch Queue將並行執行。雖然在一個Serial Dispatch Queue中同時只能執行一個追加處理,但是如果將處理分別追加到4個Serial Dispatch Queue中,各個Serial Dispatch Queue執行一個,即可以同時執行四個處理。

\

雖然這種笨辦法也可以實現並發,但是也會遇到大問題,那就是消耗大量內存:

\

 

 

(6)資源競爭問題的解決

當多個線程對同一數據進行操作時可造成競爭或者數據不一致。最簡單的解決辦法就是使用Serial Dispatch Queue。Serial Dispatch Queue只創建一個線程,而一次只能執行一個任務,只有當該任務執行結束才能去執行下一個,所以同一時間對某個競爭資源的訪問是唯一的。示意圖如下:

\

 

(7)生成的Dispatch Queue必須由程序員釋放。這是因為Dispatch Queue並沒有像Block那樣具有作為OC對象來處理的技術。通過dispatch_queue_create函數生成的Dispatch Queue在使用結束後通過dispatch_release釋放。看個下面的例子:

\

這樣立即釋放queue是否有問題呢?

在dispatch_async函數中追加Block到Dispatch Queue後,即使立即釋放Dispatch Queue,該Dispatch Queue由於被Block持有也不會被廢棄,因而Block能夠執行。Block執行結束後會釋放Dispatch Queue,這時誰都不持有Dispatch Queue,因此它會被廢棄。

 

(8)系統標准提供的Dispatch Queue

-- Main Dispatch Queue:在主線程中執行的queue,因為主線程只有一個,所以Main Dispatch Queue自然就是Serial Dispatch Queue。追加到Main Dispatch Queue的處理在主線程的RunLoop中執行。

\

 

 

-- Global Dispatch Queue:是所有應用程序都能夠使用的Concurrent Dispatch Queue,沒有必要通過dispatch_queue_create函數逐個生成Concurrent Dispatch Queue,只要獲取Global Dispatch Queue即可。其中有四個優先級,但是用於Global Dispatch Queue的線程並不能保證實時性,因此執行優先級只是大致的判斷。

對於Main Dispatch Queue和Global Dispatch Queue執行dispatch_retain函數和dispatch_release函數不會引起任何變化,也不會有任何問題。這也是獲取並使用Global Dispatch Queue比生成、使用、釋放Concurrent Dispatch Queue更輕松的原因。

 

 

(9)dispatch_set_target_queue:改變生成的Dispatch Queue的執行優先級

指定要變更執行優先級的Dispatch Queue為dispatch_set_target_queue函數的第一個參數,指定與要使用的執行優先級相同的Global Dispatch Queue為第二個參數(目標),第一個參數如果指定系統提供的Main Dispatch Queue和Global Dispatch Queue,則不會知道出現什麼狀態,因此這些均不可指定。

 

(10)dispatch_after:延遲執行

NSEC_PER_SEC:秒

NSEC_PER_MSEC:毫秒

 

(11)dispatch_barrier_async

會等待追加到Concurrent Dispatch Queue上的並行執行的處理全部結束之後,再將指定的處理追加到該Concurrent Dispatch Queue中。然後在由dispatch_barrier_async函數追加的處理執行完畢後,Concurrent Dispatch Queue才恢復為一般的動作,追加到該Concurrent Dispatch Queue的處理又開始並行執行。示意圖如下:

\..

 

(12)dispatch_async

將指定的block“非同步”的追加到指定的Dispatch Queue中,dispatch_async函數不做任何等待。

\

 

 

(13)dispatch_sync造成的問題

一旦調用dispatch_sync函數,那麼在指定的處理執行結束之前,該函數不會返回。但是dispatch_sync容易造成死鎖。

\

該源代碼在Main Dispatch Queue即主線程中執行指定的Block,並等待其執行結束。而其實在主線程中正在執行這些源代碼,所以無法執行追加到Main Dispatch Queue的Block。下面的例子也一樣:

\

Main Dispatch Queue中執行的Block等待Main Dispatch Queue中要執行的Block執行結束。當然Serial Dispatch Queue也會引起相同的問題。

\

 

(14)dispatch_apply

dispatch_apply函數是dispatch_sync函數和Dispatch Group的關聯API。該函數按指定的次數將指定的Block追加到指定的Dispatch Queue中,並等到全部處理結束。

\

 

因為在Global Dispatch Queue中執行處理,所以各個處理的執行時間不定,是不進行等待的追加任務。但是輸出結果中最後的done必定在最後的位置上。這是因為dispatch_apply函數會等待全部處理執行結束。

\

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

 

\

 

 

(15)dispatch_suspend/dispatch_resume

當追加大量處理到Dispatch Queue時,在追加處理的過程中,有時希望不執行已追加的處理。在這種情況下,只要掛起Dispatch Queue即可。當可以執行時再恢復。

-- dispatch_suspend函數掛起指定的Dispatch Queue:

dispatch_suspend(queue);

 

dispatch_resume函數恢復指定的Dispatch Queue:

dispatch_resume(queue);

 

這些函數對已經執行的處理沒有影響。掛起後,追加到Dispatch Queue中但尚未執行的處理在此之後停止執行。而恢復則使得這些處理能夠繼續執行。

 

(16)Dispatch Semaphore

當不使用信號量的時候出現的bug.

\.

這裡使用Global Dispatch Queue更新NSMutableArray類對象,所以執行後由內存錯誤導致應用程序異常結束的概率很高。

Dispatch Semaphore是持有計數信號,該計數是多線程編程中的計數類型信號。計數為0時等待,計數為1或者大於1時,減去1而不等待。

創建semaphore:

\

 

參數表示計數的初始值。

\

dispatch_semaphore_wait函數等待Dispatch Semaphore的計數值達到或者等於1.當計數值大於等於1,或者在等待中計數值大於等於1時,對該計數進行減法並從dispatch_semaphore_wait函數返回。

semaphore可以進行以下分支處理:

\

 

dispatch_semaphore_wait函數返回0時,可安全的執行需要進行排他控制的處理。該處理結束時通過dispatch_semaphore_signal函數將Dispatch Semaphore的計數數值加1.

案例:

\

\

 

 

(17)dispatch_once

dispatch_once函數是保證在應用程序執行中只執行一次指定處理的API。下面這種經常出現的用來初始化的源代碼可通過dispatch_once函數簡化:
\

 

在多核CPU中,在正在更新表示是否初始化的標志變量時讀取,就有可能多次執行初始化處理。而用dispatch_once函數初始化就不用擔心了。這就是單例模式,在生成單例對象時使用。

 

(18)GCD的基本實現與描述

蘋果官方說明:通常,應用程序中編寫的線程管理用的代碼要在系統級實現。

什麼是系統級實現?就是在iOS和macOS的核心XNU內核級上實現。因此,無論程序員如何努力編寫管理線程的代碼,在性能方面也不可能勝過XNU內核級所實現的GCD。

用於實現Dispatch Queue而使用的軟件組件:

\

Dispatch Queue沒有“取消”這一概念。一旦將處理追加到Dispatch Queue中,就沒有辦法可以將該處理刪除,也沒有辦法就執行中取消該處理。

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