GCD同時async&sync

7179 ワード

GCD同時async&sync


多くのプログラムには があり、iOS/MacOSの開発にとって、このスレッドはUI であり、このスレッドではユーザーのインタラクティブ/レンダリングに関することをします.メインスレッドにタスクを多すぎると、メインスレッドがカートンし、ユーザーが見ているのはApp応答が遅く、リストがスクロールしている間にフレームが落ちることです.タスクを複数のスレッドに分散して実行するには多くの技術があり、iOS/MacOS App開発で最も簡単で直感的なのはGCD(Grand Central Dispatch)であり、GCDは最下位のC APIである.
GCDを使用する開発者にとってスレッドの概念はなく,配布キューdispatch queueのみである.タスクはすべてblock方式で対列に提出され、GCDは自動的にCPUの使用状況に応じてスレッドプールを作成してタスクを実行し、自動的にマルチコアに実行されます.開発者はqueueの管理に参加することができず、キューはFIFO (先進先出)を採用し、先に加入したものが先に実行されることを意味し、キューは2種類ある:
  • (serial queues):asyncを呼び出してもsyncを呼び出しても、タスクは1つを終えてから次のものになり、順番に実行される.
  • (concurrent queues):タスクは同時実行です.
  • async:キューにタスクをコミットし、すぐに戻り、現在のスレッドをブロックしません.
  • sync:タスクをキューにコミットすると、タスクが終了するまで現在のスレッドがブロックされ、現在のスレッドが実行されません.
  • システムのデフォルトでは、アプリケーションごとに5つのキューがデフォルトで提供されます.1つのシリアル・プライマリ・キューと4つの同時グローバル・キューです.
  • Main Dispatch Queue( ):プライマリ・スレッドに対応し、グローバルに利用可能なシリアル・キューであり、アプリケーションのプライマリ・スレッドでタスクを実行し、このキューは一般的にAppのUIを更新するために使用される.
  • プライマリ・キューに加えて、システムはデフォルトで4つの同時キューGlobal Dispatch Queue( )を提供します.これらのキューは、アプリケーション全体でグローバルに使用可能であり、互いに優先度の高低の違いしかありません.グローバル同時キューの1つを使用するには、dispatch_を使用します.get_global_queue関数は、希望するキューの参照を取得します.この関数の最初のパラメータは、以下の値をとります.以下の優先度はSwift 3です.0ではすでに廃棄されており、QOSで代用されている
    DISPATCH_QUEUE_PRIORITY_HIGH
    DISPATCH_QUEUE_PRIORITY_DEFAULT
    DISPATCH_QUEUE_PRIORITY_LOW
    DISPATCH_QUEUE_PRIORITY_BACKGROUND
    

    これらのキュー・タイプは、実行優先度を表します.HIGHを持つキューには最高優先度があり、BACKGROUNDは最低優先度です.これにより、タスクの優先度に基づいて、どのキューを使用するかを決定できます.これらのキューもAppleのAPIで使用されているので、これらのキューにはあなた自身のタスクだけではありません.
    最後に、この5つのシステムがデフォルトで提供しているキューに加えて、シリアルまたは同時キューを自分で作成することもできます.コンカレントキューを使用する場合は、自分で作成しても、上記の4つのグローバルキューを使用することを強くお勧めします.

    QOS


    GCDとDispatchQueuesを使用する場合、Appのどのタスクが他のタスクよりも重要で、どの優先度が高いかをオペレーティングシステムに知らせる必要があります.タスクの重要度と優先度を決定する属性をGCDサービスレベル(GCD QoS)と呼ぶ.QoSは列挙タイプであり、初期キュー時に適切なQoSパラメータを提供して対応する権限を得ることができ、QoSが指定されていない場合、初期メソッドはキューが提供するデフォルトのQoS値defaultを使用する.システムは5つの優先度を提供し、上から下への優先度が順次低下します:
  • userInteractive:アニメーションなどのユーザーとインタラクティブに関連して最も優先度が高いです.例えば、ユーザが連続的にドラッグした計算
  • userInitiated:push 1つのViewController以前のデータ計算
  • default:グローバルキューGlobal Dispatch Queueを取得する場合、優先度を明確に指定しなければ、この優先度のグローバルキューに戻る.
  • utility:長い間実行して、ユーザーに結果を通知することができます.たとえば、ファイルをダウンロードして、ユーザーに進捗状況をダウンロードします.
  • background:バックグラウンドに大量のデータを格納するなど、ユーザーは表示されません.

    Swift3.0プライマリ・キュー、グローバル・キューの取得

    let mainQueue = DispatchQueue.main  //      ,   。
    let globalQueue = DispatchQueue.global() //      ,   default   
    let globalQueueWithQos = DispatchQueue.global(qos: .userInitiated) 
    

    Swfit3.0シリアル、同時キューの作成

    let serialQueue = DispatchQueue(label: "com.leo.serialQueue") //          
    let concurrentQueue = DispatchQueue(label: "com.leo.concurrentQueue",
                                attributes:.concurrent)  //            。
    

    シリアルキューのasync
    コードがPlaygroundで実行されている場合は、上部にこの2行を追加してください.

    import PlaygroundSupport
    PlaygroundPage.current.needsIndefiniteExecution = true
    public func readDataTask(label:String){
        print("Start task %@", label)
        sleep(3)
        print("  End task %@", label)
    }
    
    let serialQueue = DispatchQueue(label: "com.leo.serialQueue")
    
    print("Main queue Start")
    serialQueue.async { readDataTask(label: "1")    }  //async    
    serialQueue.async { readDataTask(label: "2")    }  //async    
    print("Main queue End")
    
    --- output
    Main queue Start
    Main queue End
    2017-03-31 11:59:38.386 MyPlayground[15660:3218113] Start task 1
    2017-03-31 11:59:40.397 MyPlayground[15660:3218113] End task 1
    2017-03-31 11:59:40.398 MyPlayground[15660:3218113] Start task 2
    2017-03-31 11:59:42.399 MyPlayground[15660:3218113] End task 2
    
  • asyncは、現在のスレッドがasyncによってブロックされないタスクをキューに送信します.だからMain queue Endはすぐに印刷されました.
  • シリアルキューであるため、asyncが呼び出されても、タスクは1つ完了してから次へと順次実行される

  • シリアルキューのsync
    public func readDataTask(label:String){
        print("Start task %@", label)
        sleep(3)
        print("  End task %@", label)
    }
    
    let serialQueue = DispatchQueue(label: "com.leo.serialQueue")
    
    print("Main queue Start")
    serialQueue.sync { readDataTask(label: "1")    } //sync       ,         
    serialQueue.sync { readDataTask(label: "2")    } //sync       ,         
    
    print("Main queue End")
    
    --- output
    Main queue Start
    2017-03-31 12:30:54.915 MyPlayground[16030:3294545] Start task 1
    2017-03-31 12:30:57.917 MyPlayground[16030:3294545]   End task 1
    2017-03-31 12:30:57.918 MyPlayground[16030:3294545] Start task 2
    2017-03-31 12:31:00.919 MyPlayground[16030:3294545]   End task 2
    Main queue End
    
  • syncは、タスクが完了するまで現在のスレッドをブロックします.したがって、Main queue Endは、前の2つのタスクが完了するまで印刷されません.
  • シリアルキューであるため、タスクが1つ完了してから次へと順次実行される.

  • 同時キューasync
    public func readDataTask(label:String){
        print("Start task %@", label)
        sleep(3)
        print("  End task %@", label)
    }
    
    let concurrentQueue = DispatchQueue(label: "com.leo.concurrentQueue",
                                        attributes: .concurrent) //    
    
    print("Main queue Start")
    concurrentQueue.async { readDataTask(label: "1")    }
    concurrentQueue.async { readDataTask(label: "2")    }
    print("Main queue End")
    
    --- output
    Main queue Start
    Main queue End
    2017-03-31 14:23:33.705 MyPlayground[16602:3412412] Start task 2
    2017-03-31 14:23:33.705 MyPlayground[16602:3412397] Start task 1
    2017-03-31 14:23:36.709 MyPlayground[16602:3412397]   End task 1
    2017-03-31 14:23:36.709 MyPlayground[16602:3412412]   End task 2
    
    
  • asyncは、現在のスレッドがasyncによってブロックされないタスクをキューに送信します.だからMain queue Endはすぐに印刷されました.
  • 同時キューであるため、タスクは同時実行である.

  • 同時キューsyncは、実行結果がシリアルキューのsyncと同じであることを感じます.
    public func readDataTask(label:String){
        print("Start task %@", label)
        sleep(3)
        print("  End task %@", label)
    }
    
    let concurrentQueue = DispatchQueue(label: "com.leo.concurrentQueue",
                                        attributes: .concurrent)
    
    print("Main queue Start")
    concurrentQueue.sync { readDataTask(label: "1")    }
    concurrentQueue.sync { readDataTask(label: "2")    }
    print("Main queue End")
    
    --- output
    Main queue Start
    2017-03-31 15:48:39.672 MyPlayground[17360:3609929] Start task 1
    2017-03-31 15:48:42.673 MyPlayground[17360:3609929]   End task 1
    2017-03-31 15:48:42.674 MyPlayground[17360:3609929] Start task 2
    2017-03-31 15:48:45.675 MyPlayground[17360:3609929]   End task 2
    Main queue End
    
    
  • syncは、タスクが完了するまで現在のスレッドをブロックします.したがって、Main queue Endは、前の2つのタスクが完了するまで印刷されません.
  • は同時キューですがsyncメソッドが呼び出され、現在のスレッドがブロックされ、タスクは同時実行できません.実行結果はシリアルキューのsyncと同じです