GCD理解

9241 ワード

GCD


GCD(Grand Central Dispatch)はlibdispatchの市場名であり、libdispatchはAppleのライブラリとして、マルチコアハードウェア上での同時コードの実行に強力なサポートを提供しています.iOSマルチスレッドプログラミングでは、GCDがかなり重要な地位を占めています.NSThreadとNSOperationに比べてGCDの使用が簡単で便利で、本格的なハードウェアマルチコアサポートを実現しているため、開発者は実行したいタスクを定義し、適切なDispatch Queueに追加するだけです.

dispatch_queue


dispatch_Queueは処理を実行する待機キューであり、FIFo(先進先出)キューであり、先にキューに入れたタスクが先に実行されることを保証する.
dispatch_Queueはスレッドが安全で、同時タスクで呼び出されたときにタスクの問題は発生しません.安心して使えます.
dispatch_queuタイプ:
1)シリアルdispatch_Queueはシリアルキューで一度に1つのタスクのみを実行し、キューに追加する順序で実行します.
シリアルキューでは、同じ時間間隔で1つのタスクしか実行できません.前のタスクが終了すると、次のタスクが開始されます.
2)同時dispatch_Queueは、同時キューで1回に複数実行でき、追加された順序で実行が開始されます.
同時キュー内の同じ時間間隔で複数のタスクを同時に実行できます.
コンカレントキュー内のタスクは、現在実行中の処理の終了を待つことなく、複数の処理を並列に実行できますが、並列に実行される処理の数は、現在のシステムステータスに依存します.すなわちiOSおよびOS Xは、dispatch Queueにおける処理数、cpuコアおよびCPU負荷などの現在のシステムの状態に基づいて、Concurrent Dispatch Queueで実行される処理数を決定する.
  • パラレル:同じ時間に複数のタスクが実行される
  • 同時実行:同じ時間間隔で複数のタスクが
  • を実行する.
    コンカレントは、複数のタスクの実行を許可する能力であると簡単に理解できます.パラレルは、実際に同じ時間に複数のタスクを実行します.並列できるようにするには、まず同時でなければなりません.しかし、並列は必要ありません.パラレルはマルチコアで実行するのが比較的容易で、各コアにスレッドを割り当ててそれぞれのタスクを実行することができます.しかし,単一コアで並列を実行するのは容易ではなく,単一コアではタイムスライス回転法を用いなければならない.例えば、2つのスレッドThread 1,Thread 2がある、1つのタイムスライス内でまずThread 1のタスクを実行し、その後現場を保持する、次のタイムスライスでThread 2を実行し、タイムスライスが終了すると現場を保持してThrad 1に切り替える次のタイムスライスを開始する.このタイムスライスはかなり短いので、同時に実行しているような感じがします.
    dispatch_queu作成
  • CGD API
  • を介して
    func dispatch_queue_create(_ label: UnsafePointer, _ attr: dispatch_queue_attr_t!) -> dispatch_queue_t!

    毎回dispatch_を呼び出すqueue_createは新しいdispatchを作成します.queue_tと新しいスレッドなので、dispatch_を作成しすぎます.Queueはスレッドを作成しすぎると、メモリが大量に消費され、コンテキストの切り替えが大量に発生します.これにより、複数のスレッドのqueueが並列タスクキューになり、同時にデータを書くと問題が発生する可能性があります.
    2.システムに既に存在するdispatch_を取得するqueue
    Main Dispatch Queue:メインスレッドで実行されるシリアルdispatch_Queueは、メインスレッドが1つしかないので、Main Dispatch Queueも1つしか存在しません.Main Dispatchのすべてのタスクは、NSObjectのperformSelectOnMainThreadと同様に、プライマリスレッドのrunloopで実行されます.したがって、UI更新等のメインスレッドで実行する必要があるタスクは、Main Dispatch queueに追加する必要がある.
    Global Dispatch Queue:グローバルで利用可能なパラレルキュー.キューには4つの実行優先順位があります.High Priority, Default Low, BackGround.

    実戦使用

  • サブスレッドで実行する時間のかかる動作は、インタフェースをブロックすることを回避し、メインスレッドでインタフェース
  • を更新する.
    
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        dispatch_async(mainQueue, ^{
             //  UI
        });
    
        dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
        dispatch_async(globalQueue, ^{
            //  
        });
    
            dispatch_async(dispatch_get_global_queue(0, 0), { () -> Void in
                let url:NSURL = NSURL(string:"http://7xlgnt.com1.z0.glb.clouddn.com/Serial.png")!
                let imageData: NSData? = NSData(contentsOfURL:url)
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    if let imageData = imageData {
                        imageView.image = UIImage(data: imageData)
                    }
                })
    
            })
    
  • 遅延操作dispatch_after
  • 
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0)) { () -> Void in
                print("come here")
            }
    
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_MSEC)), dispatch_get_global_queue(0, 0)) { () -> Void in
                print("come here2")
            }
    
    
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_USEC)), dispatch_get_global_queue(0, 0)) { () -> Void in
                        print("come here3")
                    }

    まずいくつかの値を見てみましょう
    print(NSEC_PER_SEC)結果:10000000ピコ秒10000000=1秒
    print(NSEC_PER_MSEC)結果:100000微妙100000=1秒
    print(NSEC_PER_USEC)結果:1000ミリ秒1000ミリ秒=1秒
    print(DISPATCH_TIME_NOW) 0
    print(DISPATCH_TIME_FOREVER) 18446744073709551615
    print(UInt64.max) 18446744073709551615
    public func dispatch_time(when: dispatch_time_t, _ delta: Int64) -> dispatch_time_t
    Whenはいつ遅延を計算し始めたのか、UIT 64の値で、ここの値はピコ秒です.
    _deltaは何ピコ秒遅延して実行しますか.
    1秒遅れるには
    dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_SEC) dispatch_time(DISPATCH_TIME_NOW,Int64(1000 * NSEC_PER_MSEC) dispatch_time(DISPATCH_TIME_NOW,Int64(1000000 * NSEC_PER_USEC)
    上記のコードの実行結果は次のとおりです.
    come here3
    come here2
    come here
  • は、一度だけ実行されるdispatch_を保証する.once

  • public func dispatch_once(predicate: UnsafeMutablePointer, _ block: dispatch_block_t)
    単一の例は、コードで最も一般的な1回のみ実行される例です.次のクラスがあります.
    
    class Car {
    
        var name:String?
        static var car:Car?
        class func shareInstance() -> Car {
            if (car == nil) {
                car = Car()
                print(" ")
            }
            return car!
        }
    }

    上記のクラスにはshareInstanceのメソッドがあり、単一のオブジェクトを生成しますが、上記のメソッドはスレッドが安全ではありません.マルチスレッドで上記のメソッドを実行すると問題が発生します.
    
            dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
                var car = Car.shareInstance()
            };
    
            dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
                var car = Car.shareInstance()
            };
    
            dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
                var car = Car.shareInstance()
            };
    

    実行時に出力が3回「初期化開始」されていることがわかります.これはcar==nilが現在初期化が開始されているかどうかを有効に判断できないことを示しています.1つ目の初期化がまだ完了していない場合、2つ目の初期化が開始され、3つ目も初期化が開始されているため、3つの「初期化開始」が出力されていることを示しています.
    
    class Car {
    
        var name:String?
    
        class var shareInstance:Car {
            struct Static {
                static var car:Car?
                static var onceToken : dispatch_once_t = 0
            }
    
            dispatch_once(&Static.onceToken) { () -> Void in
                Static.car = Car()
            }
            return Static.car!
        }
    }
    

    上のコードに変えてdispatch_を使うとonceは、スレッドになって安全になります.