日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

为什么要用GCD-Swift2.x

發布時間:2025/3/21 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 为什么要用GCD-Swift2.x 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

當今世界,多核已然普及。但是APP卻不見得很好的跟上了這個趨勢。APP?
想要利用好多核就必須可以保證任務能有效的分配。并行執行可以讓APP同時執行很多?
的任務。這個其實很難,但是有了GCD一切都變得簡單了很多。

你并不是一定要寫一個大并發的APP才需要用GCD。使用GCD可以讓你的APP更快的?
響應用戶的操作,不用要等到你的UI或者服務等到執行完成。一般來說你會把各種任務?
都分配給其他核心去執行,而你的主線程(UI線程)可以隨時處理用戶的操作。?
GCD可以讓這些變得簡單。

線程

傳統的多任務分發方式是使用線程。在一個多核的設備上,每一個新的線程都可以被分配在一個CPU核心?
上,與其他的線程并行執行。

單核心的CPU麻煩一些,系統會不停地在幾個線程之間切換以保證每個線程都有機會執行。這樣做的效果 是看起來像是并行執行的,但是其實多個線程的不同任務之間是順序執行的。

但是使用線程也會遇到一個很大的問題。數據在線程之間的正確傳遞就是一個很大的難題了。線程之間的?
同步和互斥又會變得很詭異難以調試。而且,為了保證APP的高效快速運行,開辟多少線程也是一個需要思考的?
問題。因為,創建和銷毀線程也是有很大的資源消耗的。于是很多的系統都提供了一個叫做線程池?
的概念來解決這個問題。所有的線程創建后都放在這個池子里統一管理。

同步

當你有多個線程在執行的時候,你一般都會遇到一個問題Race Condition,實際的運算結果?
取決于那個線程先獲得共享數據。一個經典的例子就是:銀行賬戶問題。

class BankAccount {var balance: Double?//... }// 創建一個賬號,給這個賬號存100塊 var account = BankAccount() account.balance = 100// 第一個線程:取10塊 func withdraw() {let balance = account.balanceaccount.balance = balance! - 10 }// 第二個線程:增加10%的利息 func accrue() {let balance = account.balanceaccount.balance = balance! * 1.10 }// 那么最后:account.balance = ?

最后的結果取決于這兩個線程哪一個先執行。在并行的條件下執行的先后順序是不定的。但是執行順序不同?
最后的balance值就是不同的。

上面的代碼會有多少個可能的不同結果?balance都會是什么值?
  • 1
Race Condition即使在單核設備下也會發生。
  • 1

對于這些問題,傳統的解決方法如下:?
* 信號量(semaphore)- 用來控制一組有限資源的消費。線程等待,知道資源可用。?
* 互斥(Mutex)- 一次只允許一個線程執行。當一個線程持有mutex的時候其他線程等待。?
* 條件變量(Condvar)- 線程等待直到某些條件為真。

隊列

GCD把線程的創建、回收以及線程的同步等進一步抽象為隊列(Queue)統一管理。

隊列,簡單而言,就是一個可以讓數據按照先進先出的順序執行。一個APP可以創建多個隊列,并且多個?
隊列之間可以并行的處理各自的任務,每個隊列內部的任務順序執行。

隊列比線程有很多的優勢。第一,GCD庫屏蔽了線程管理的繁瑣部分。隊列會在需要的時候自動創建線程?
并且在不需要的時候回收。其次,GCD庫會根據系統的CPU核心數創建最佳數量的線程。最后,隊列只會?
在需要的時候創建線程,所以資源利用會得到優化。

總之,隊列給你了你線程能給的,但是又不用考慮具體線程的操作。

GCD有三種隊列:?
* 順序隊列(Serial)- 每次執行一個任務,按照任務加入隊列的順序。一個任務執行完成后執行下一個。?
* 并發(Concurrent)- 按照任務加入隊列的順序開始,但是后面的任務不用等到前面的任務執行完成就可以開始。?
* 主線程(Main)- 一個預先開啟的序列線程。這個隊列中包含一個NSRunLoop實例。你的APP總是在這個隊列中運行。

系統提供了幾種并發隊列。這些隊列有自己專屬的QoS(服務質量種類)。這個服務質量種類是用來表示你提交的?
任務的意圖是什么,這樣GCD可以有針對性的優化。?

  • QOS_CLASS_USER_INTERACTIVE?這個用戶交互(user interactive)表示任務需要立即執行,以便APP?

? ? ? ? 給用戶一個良好的用戶體驗。一般用于更新UI、處理事件和小延遲的處理。在你的整個APP中,這以種類的任務應該保持?

? ? ? ? 一個較低的總量。

  • QOS_CLASS_USER_INITIATED用戶初始化(user initiated)表示任務是在UI初始化的,并且可以?
    異步執行。這個一般用于處理用戶等待的需要理解給出運行結果,或者任務需要理解完成用戶交互的情況。

  • QOS_CLASS_UTILITY通用(utility)表示長期執行的任務,一般來說用戶可以見到任務執行的比例。?
    一般用來處理大的計算、I/O、網絡以及不簡單的數據提交等類似任務。這一種類做了電量優化。

  • QOS_CLASS_BACKGROUND后臺運行(background)表示用戶并不知道任務在運行中。這個一般用于?
    數據的提前加載、維護,以及其他無需用戶交互和任務完成時間無明顯限制的情況。

這里需要注意一點,蘋果的API也會用到這些全局分發的隊列。所以,在這些隊列里并不是只有你的任務在執行。當然,?
如前文所述你也可以創建你自己的隊列。你可以選擇4種全局隊列,主隊列,還有兩種自定義隊列可以選擇。

Closure

隊列的任務使用closure封裝。

你也可以使用方法加入隊列,不過這里只使用block。
  • 1

這里有一個closure的例子,讓你對closure有一個大概的印象。swift的closure就和Objective-C的block?
差不多。

func makeIncrementer() -> ((Int) -> Int) {func addOne(number: Int) -> Int {return 1 + number}return addOne }var increment = makeIncrementer()increment(9)

上面的例子makeIncrementer返回一個closure,這個closure需要一個整型參數。

Hello dispatch world!

這里需要注意一點:Objective-C的block和Swift的closure。?
Swift的closure和Objective-C的block是兼容的。所以你完全可以把Swift的closure傳入Objective-C?
中需要block參數的方法中。Swift的closure和方法是同一類型的,所以你甚至可以把swift的方法名傳遞過去。

Closure和block的上下文處理語法基本一樣。唯一不同的是變量在closure中直接就是可變的。也就是說Objective-C?
中的__block關鍵字在Swift的closure中是默認行為。

dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), {print("hello dispatch:- user interactive") })dispatch_async(dispatch_get_main_queue(), {print("hello dispatch:- main queue") })

調用dispatch_async(),GCD就會給隊列添加一個closure任務,然后繼續代碼的執行完全不會等待closure?
的結束。在我們的例子中,第一次dispatch_async是給QOS_CLASS_USER_INTERACTIVE的類型的全局?
隊列添加了一個任務。第二次是給dispatch_get_main_queue,也就是主線程添加了一個任務。

下面看一個自創隊列的例子:

@IBAction func concurrentAction(sender: AnyObject) {let concurrentQueue = dispatch_queue_create("concurrent.test.queue", DISPATCH_QUEUE_CONCURRENT) //1for i in 0...1000 { dispatch_async(concurrentQueue){ //2NSThread.sleepForTimeInterval(1) //3 print("print concurrent queue:- \(i)") //4}} }

這里一步一步的介紹一下:?
1. 創建一個名稱為concurrent.test.queue的并發隊列。?
2. 做一個1001次的循環,每個循環給這個隊列添加一個closure。以上的寫法只是一個簡寫,其實是這樣的:

dispatch_async(concurrentQueue,{ //2NSThread.sleepForTimeInterval(1) //3print("print concurrent queue:- \(i)") })
  • 當前線程休眠一秒
  • Barriers

    很多人看到這里就會想,并發隊列在哪兒體現出來隊列的概念了呢?這分明就是一個把一堆closure扔進去分開執行的堆或者Set(集合)?
    而已嘛。

    目前來看是的,但是當你遇到barrier的時候對列就真的變成隊列了。你可以使用dispatch_barrier_sync和?
    dispatch_barrier_async兩個方法把closure加入隊列中。這個時候就很有意思了。扔進去的closure并?
    不會立刻執行,而是要等。等到在這個closure之前扔進隊列的全部closure都執行完成之后才開始執行。然后,?
    在這個barrier的closure執行完成之后,在它后面扔進隊列的closure才會被執行??梢园裝arrier的closure認為?
    是一個特殊的點,在這個點的從前到后都是順序執行的。除此之外的點,還是并行執行。

    我們來看看具體的例子:

    let concurrentQueue = dispatch_queue_create("concurrent.test.queue", DISPATCH_QUEUE_CONCURRENT)var count: Int = 0for _ in 0...100 {dispatch_async(concurrentQueue){NSThread.sleepForTimeInterval(1)print("print concurrent queue:- \(count++)")} }dispatch_barrier_async(concurrentQueue, {print("##ASYNC in barrier, concurrent queue - START")for _ in 1...10 {NSThread.sleepForTimeInterval(0.5)}print("##ASYNC in barrier, concurrent queue - END") })for _ in 0...100 {dispatch_async(concurrentQueue){NSThread.sleepForTimeInterval(1)print("print concurrent queue:- \(count++)")} }dispatch_barrier_sync(concurrentQueue, {print("##SYNC in barrier, concurrent queue - START")for _ in 1...10 {NSThread.sleepForTimeInterval(0.5)}print("##SYNC in barrier, concurrent queue - END") })for _ in 0...100 {dispatch_async(concurrentQueue){NSThread.sleepForTimeInterval(1)print("print concurrent queue:- \(count++)")} }

    注意barrier最好是用在自己創建的并發隊列上。否則的話dispatch_barrier_async的效果就和dispatch_async一樣,而?

    dispatch_barrier_sync就和dispatch_sync一樣。dispatch_barrier_sync要分配的隊列是當前隊列?
    的話會造成死鎖。

    讀寫鎖

    先看一個新聞累的單例的例子。這個例子的最初形態中不是一個線程安全的單例:

    class NewsFeed {static let sharedInstance = NewsFeed() //1private init() {} //2private var _news: [String] = []var news: [String] {return _news}func addNews(n: String) {_news.append(n)} }

    ?

    有這么一個新聞的類。?
    1. 實現一個單例。Swift的單例明顯要比Objective-C簡單了很多。是的,Swift的static屬性自動內置了?
    dispatch_once。所以,這么一樣就實現了Swift的單例。?
    2.?init方法只能給類的內部調用,但是不能給外部在調用,否則就不是單例了。

    用戶可以調用news屬性來讀取全部的新聞,也可以通過調用方法addNews來添加新聞。?
    當時當多個線程都可以訪問addNews方法的時候,那么news屬性讀出來的新聞列表就有很大的可能是錯的。?
    我們現在使用barrier來確保這個功能可以正確的執行。

    解決辦法就是任何的線程要添加新聞,那么就必須通過barrier這一道關口。在添加新聞的時候只有一個closure執行。?
    其他的添加closure等待。

    為了保證線程的安全,讀取新聞的操作也只能在concurrentQueue上執行。但是這次我們是需要立即返回結果的?
    沒法使用dispatch_async。這個時候使用dispatch_sync就是最好的選擇了。使用dispatch_sync?
    可以等到方法執行完畢,并返回我們需要的結果。dispatch sync可以知道dispatch barrier的進度如何,添加了?
    幾條新聞。也可以讓closure執行完成并返回。

    但是使用dispatch sync需要很小心。如前所述,如果你給dispatch sync分配的隊列是當前正在運行的隊列?
    會造成死鎖。因為調用會等待這個closure結束,這個closure甚至可能都沒法開始。因為這個closure需要等到?
    它前面運行的closure結束之后才能開始。

    1 2 3 4 5 6 7 8 private?var?_news: [String] = [] var?news: [String] { ????var?newsCopy: [String]! ????dispatch_sync(concurrentQueue){?//1 ????????newsCopy?=?self._news?//2 ????} ????return?newsCopy }

    下面逐一解釋:?
    1. dispatch sync同步分配到concurrentQueue上執行讀取操作。?
    2. 保存一份新聞的拷貝給newsCopy并返回。

    現在這個新聞單例就是線程安全的了。下面我們看一下完整的代碼:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import?Foundation class?NewsFeed?{ ????static?let?sharedInstance?=?NewsFeed() ????private?let?concurrentQueue?=?dispatch_queue_create("newsfeed.queue.concurrent", ????????DISPATCH_QUEUE_CONCURRENT) ????private?init() {} ????private?var?_news: [String] = [] ????var?news: [String] { ????????var?newsCopy: [String]! ????????dispatch_sync(concurrentQueue){ ????????????newsCopy?=?self._news ????????} ????????return?newsCopy ????} ????func?addNews(n:?String) { ????????dispatch_barrier_async(concurrentQueue){ ????????????self._news.append(n) ????????????dispatch_async(dispatch_get_main_queue()){ ????????????????self.newsAddedNotification() ????????????} ????????} ????} ????func?newsAddedNotification() { ????????// post notification ????} }

      

















    本文轉自張昺華-sky博客園博客,原文鏈接:http://www.cnblogs.com/sunshine-anycall/p/5126014.html,如需轉載請自行聯系原作者

    總結

    以上是生活随笔為你收集整理的为什么要用GCD-Swift2.x的全部內容,希望文章能夠幫你解決所遇到的問題。

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