GCDの並行キューとシリアルキュー


117

GCDの並行キューとシリアルキューを完全に理解するのに苦労しています。私にはいくつかの問題があり、誰かがはっきりとその時点で私に答えてくれることを願っています。

  1. 私は、シリアルキューが次々とタスクを実行するために作成および使用されることを読んでいます。ただし、次の場合はどうなりますか。

    • シリアルキューを作成します
    • dispatch_async(作成したばかりのシリアルキューで)3回使用して、3つのブロックA、B、Cをディスパッチします

    3つのブロックが実行されますか:

    • キューがシリアルであるため、順序A、B、C

      または

    • 並行して(並列スレッドで同時に)、ASYNCディスパッチを使用したため
  2. dispatch_syncブロックを次々に実行するために並行キューで使用できることを読んでいます。その場合、シリアルキューが存在するのはなぜですか?必要なだけブロックをSYNCHRONOUSLYディスパッチできるコンカレントキューを常に使用できるので、なぜですか?

    良い説明ありがとうございます!


シンプルで適切な前提条件の質問ディスパッチ同期vs非同期
ハニー

回答:


216

簡単な例:実行に1分かかるブロックがあります。メインスレッドからキューに追加します。4つのケースを見てみましょう。

  • async-同時実行:コードはバックグラウンドスレッドで実行されます。コントロールはメインスレッド(およびUI)にすぐに戻ります。ブロックは、そのキューで実行されている唯一のブロックであるとは想定できません。
  • async-シリアル:コードはバックグラウンドスレッドで実行されます。制御は直ちにメインスレッドに戻ります。ブロック、そのキューで実行されている唯一のブロックであると想定できます
  • 同期-同時実行:コードはバックグラウンドスレッドで実行されますが、メインスレッドはそれが完了するまで待機し、UIへの更新をブロックします。ブロックは、そのキューで実行されている唯一のブロックであるとは想定できません(数秒前に非同期を使用して別のブロックを追加することもできます)。
  • 同期-シリアル:コードはバックグラウンドスレッドで実行されますが、メインスレッドはそれが完了するまで待機し、UIの更新をブロックします。ブロック、そのキューで実行されている唯一のブロックであると想定できます

明らかに、実行時間の長いプロセスには、最後の2つは使用しません。通常は、別のスレッドで実行されている可能性があるものから(常にメインスレッドで)UIを更新しようとしているときに表示されます。


14
(1)キューのタイプ(concまたはserial)は、タスクを順番に実行するか並列で実行するかを決定する唯一の要素です。(2)ディスパッチタイプ(同期または非同期)は、実行が進むか、次の命令に行かないかだけを言っていますか?つまり、タスクSYNCをディスパッチすると、実行されるキューに関係なく、コードはそのタスクが完了するまでブロックされますか?
ボグダンアレクサンドル

13
@BogdanAlexandru正解です。キューは、ブロックをキューに入れる方法ではなく、実行ポリシーを決定します。同期はブロックが完了するまで待機しますが、非同期は完了しません。
Jano、2013年

2
@swiftBUTCHERある時点まで、はい。キューを作成するときに、スレッドの最大数を指定できます。追加するタスクがそれよりも少ない場合、タスクは並行して実行されます。それ以上の場合、一部のタスクは使用可能な容量がなくなるまでキューに残ります。
Stephen Darlington 2016年

2
@PabloA。、メインスレッドはシリアルキューなので、実際には2つのケースしかありません。それ以外はまったく同じです。非同期はすぐに戻ります(ブロックはおそらく現在の実行ループの最後に実行されます)。あなたが同期を行う場合、メイン落とし穴があるからメインスレッドあなたがデッドロックを取得した場合には、メインスレッド、。
スティーブンダーリントン

1
@ShauketSheikhいいえ。メインスレッドはシリアルキューですが、すべてのシリアルキューがメインスレッドであるとは限りません。4番目のポイントでは、メインスレッドがブロックし、別のスレッドが作業を完了するのを待ちます。シリアルキューがメインスレッドである場合、デッドロックが発生します。
スティーブンダーリントン

122

ここで私は、私はこれらについて理解させるために行っている実験のカップルですserialconcurrentとキューはGrand Central Dispatch

 func doLongAsyncTaskInSerialQueue() {

   let serialQueue = DispatchQueue(label: "com.queue.Serial")
      for i in 1...5 {
        serialQueue.async {

            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
    }
}

GCDで非同期を使用すると、タスクは別のスレッド(メインスレッド以外)で実行されます。非同期とは、次の行の実行がブロックの実行まで待機しないことを意味します。これにより、メインスレッドとメインキューがブロックされなくなります。シリアルキューなので、すべてがシリアルキューに追加された順序で実行されます。シリアルに実行されるタスクは、キューに関連付けられた単一のスレッドによって常に一度に1つずつ実行されます。

func doLongSyncTaskInSerialQueue() {
    let serialQueue = DispatchQueue(label: "com.queue.Serial")
    for i in 1...5 {
        serialQueue.sync {
            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
    }
}

GCDで同期を使用すると、タスクがメインスレッドで実行されることがあります。同期は、指定されたキューでブロックを実行し、ブロックが完了するのを待って、メインスレッドまたはメインキューをブロックします。メインキューは、ディスパッチされたブロックが完了するまで待機する必要があるため、メインスレッドは、メインキュー。したがって、バックグラウンドキューで実行されているコードが実際にメインスレッドで実行されている可能性があります。シリアルキューなので、すべてが追加された順序で実行されます(FIFO)。

func doLongASyncTaskInConcurrentQueue() {
    let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
    for i in 1...5 {
        concurrentQueue.async {
            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
        print("\(i) executing")
    }
}

GCDで非同期を使用すると、タスクはバックグラウンドスレッドで実行されます。非同期とは、次の行の実行が、ブロックが実行されるまで待機しないことを意味します。並行キューでは、タスクはキューに追加された順序で処理されますが、キューに接続されているスレッドは異なります。タスクがキューに追加される順序はタスクを完了するとは限らないことに注意してください。タスクの順序は、スレッドが作成されるたびに異なります。それ以上(maxConcurrentOperationCount)に達すると、一部のタスクはスレッドが解放されるまでシリアルとして動作します。

func doLongSyncTaskInConcurrentQueue() {
  let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
    for i in 1...5 {
        concurrentQueue.sync {
            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
        print("\(i) executed")
    }
}

GCDで同期を使用すると、タスクがメインスレッドで実行されることがあります。同期は、指定されたキューでブロックを実行し、ブロックが完了するのを待って、メインスレッドまたはメインキューをブロックします。メインキューは、ディスパッチされたブロックが完了するまで待機する必要があるため、メインスレッドは、メインキュー。したがって、バックグラウンドキューで実行されているコードが実際にメインスレッドで実行されている可能性があります。同時キューなので、タスクはキューに追加された順序で終了しない場合があります。ただし、同期操作では、別のスレッドで処理される可能性がありますが、同期操作では実行されます。したがって、これはシリアルキューであるため、動作します。

これらの実験の概要は次のとおりです

GCDを使用すると、タスクをキューに追加し、そのキューからタスクを実行するだけです。キューは、操作が同期か非同期かに応じて、メインまたはバックグラウンドスレッドでタスクをディスパッチします。キューのタイプは、シリアル、コンカレント、メインディスパッチキューです。実行するすべてのタスクは、デフォルトでメインディスパッチキューから実行されます。アプリケーションで使用する定義済みのグローバルコンカレントキューが4つ、メインキューが1つ(DispatchQueue.main)あります。また、独自のキューを手動で作成し、そのキューからタスクを実行することもできます。

UI関連タスクは、メインキューにタスクをディスパッチすることにより、常にメインスレッドから実行する必要があります。ショートハンドユーティリティはDispatchQueue.main.sync/async、メインまたはバックグラウンドのどちらを使用していても、ネットワーク関連/重い操作は常に非同期で実行する必要があります。

編集:ただし、場合によっては、UIをフリーズせずにバックグラウンドスレッドで同期的にネットワーク呼び出し操作を実行する必要があります(OAuthトークンを更新し、成功するかどうかを待機します)。非同期操作内にそのメソッドをラップする必要があります。操作は順序どおりに、メインスレッドをブロックせずに実行されます。

func doMultipleSyncTaskWithinAsynchronousOperation() {
    let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
    concurrentQueue.async {
        let concurrentQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
        for i in 1...5 {
            concurrentQueue.sync {
                let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
                let _ = try! Data(contentsOf: imgURL)
                print("\(i) completed downloading")
            }
            print("\(i) executed")
        }
    }
}

編集編集:ここでデモビデオを見ることができます


素晴らしいデモンストレーション.... 次の行はブロックが実行されるまで待機しないため、メインスレッドはブロックされません。このため、バックグラウンドスレッドでブレークポイントを使用}すると、その時点では実際には実行されていないため、ジャンプします
Honey

@That lazy iOS Guy a非同期コンカレントと非同期シリアルの違いはまだわかりません。どちらを使用することの意味は何ですか。どちらもバックグラウンドで実行され、UIに影響を与えません。そして、なぜ同期を使用するのですか?すべてのコードが同期しているわけではありません。次々?
eonist 2017

1
@GitSyncAppあなたはここで
Anish Parajuli웃

@その怠惰なiOSの人웃:それを作るためのthx。slack swift-langに投稿しました。DispatchGroupとDispatchWorkItemについても作成できる場合は、:D
eonist

私はあなたの最後の1つをテストしましたconcurrentQueue.syncdoLongSyncTaskInConcurrentQueue()機能の、それはメインスレッドを印刷します、Task will run in different thread本当ではないようです。
ガブラー

54

まず、スレッドとキューの違い、およびGCDが実際に行うことを理解することが重要です。(GCDを介して)ディスパッチキューを使用する場合、スレッドではなく、実際にキューに入れられます。ディスパッチフレームワークは、Appleが「正しいスレッドソリューションを実装することが、不可能ではないにしても、不可能ではないにしても、非常に困難になる可能性がある」ことを認めているため、スレッドから遠ざけるために特別に設計されました。したがって、タスク(UIをフリーズさせたくないタスク)を同時に実行するには、それらのタスクのキューを作成してGCDに渡すだけです。また、GCDは関連するすべてのスレッドを処理します。したがって、実際に行っているのはキューイングだけです。

すぐに知っておくべき2番目のことは、タスクとは何かです。タスクとは、そのキューブロック内のすべてのコードです(キュー内ではなく、常にキューに何かを追加できるため、キューに追加したクロージャ内にあります)。タスクはブロックと呼ばれることもあり、ブロックはタスクと呼ばれることもあります(ただし、特にSwiftコミュニティでは、タスクとして一般的に知られています)。また、コードの量に関係なく、中括弧内のすべてのコードは単一のタスクと見なされます。

serialQueue.async {
    // this is one task
    // it can be any number of lines with any number of methods
}
serialQueue.async {
    // this is another task added to the same queue
    // this queue now has two tasks
}

また、同時実行とは単に他のことと同時に実行することを意味し、シリアルとは次々と(同時に実行することはない)ことを意味することは明らかです。何かをシリアル化する、または何かをシリアルに配置するということは、左から右へ、上から下へ、中断することなく、最初から最後までその順序で実行することを意味します。

キューには、シリアルと同時の2つのタイプがありますが、すべてのキューは相互に同時です。「バックグラウンドで」コードを実行したいということは、別のスレッド(通常はメインスレッド)と同時にコードを実行したいということです。したがって、すべてのディスパッチキューは、シリアルまたは同時で、他のキュー比較してタスクを同時に実行します。キューによって(シリアルキューによって)実行されるシリアル化は、その単一の[シリアル]ディスパッチキュー内のタスクにのみ関係します(同じシリアルキュー内に2つのタスクがある上記の例のように、これらのタスクは後で実行されます)もう一方は、決して同時には行いません)。

シリアルキュー(プライベートディスパッチキューとも呼ばれます)は、特定のキューに追加された順序で、開始から終了まで一度に1つずつタスクの実行を保証します。これは、ディスパッチキューの説明のどこかでのシリアル化の唯一の保証です。-特定のシリアルキュー内の特定のタスクがシリアルで実行されること。ただし、繰り返しますが、すべてのキューが相互に並行しているため、シリアルキューは、別のキューである場合、他のシリアルキューと同時に実行できます。すべてのタスクは個別のスレッドで実行されますが、すべてのタスクが同じスレッドで実行されることが保証されているわけではありません(重要ではありませんが、知っておくと興味深いでしょう)。また、iOSフレームワークにはすぐに使用できるシリアルキューは付属していません。作成する必要があります。プライベート(非グローバル)キューはデフォルトでシリアルであるため、シリアルキューを作成するには:

let serialQueue = DispatchQueue(label: "serial")

属性プロパティを使用して、コンカレントにすることができます。

let concurrentQueue = DispatchQueue(label: "concurrent", attributes: [.concurrent])

ただし、この時点で、プライベートキューに他の属性を追加しない場合は、すぐに使用できるグローバルキュー(すべて同時)の1つを使用することをお勧めします。この回答の下部には、(targetプロパティを使用して)シリアルキューを作成する別の方法が表示されます。これは、Appleが(より効率的なリソース管理のために)実行することをお勧めする方法です。しかし、現時点では、ラベルを付けるだけで十分です。

CONCURRENT QUEUES(多くの場合、グローバルディスパッチキュー)は、タスクを同時に実行できます。ただし、タスクは特定のキューに追加れた順序で開始すること保証されていますが、シリアルキューとは異なり、キューは最初のタスクが完了するのを待たずに2番目のタスクを開始します。タスク(シリアルキューと同様)は個別のスレッドで実行され、(シリアルキューと同様)すべてのタスクが同じスレッドで実行されることが保証されているわけではありません(重要ではありませんが、知っておくと興味深いでしょう)。また、iOSフレームワークには、すぐに使用できる4つの同時キューが付属しています。上記の例を使用するか、Appleのグローバルキューのいずれかを使用して、コンカレントキューを作成できます(通常はこれをお勧めします)。

let concurrentQueue = DispatchQueue.global(qos: .default)

RETAIN-CYCLE RESISTANT:ディスパッチキューは参照カウントオブジェクトですが、グローバルキューはグローバルであるため保持および解放する必要はなく、保持および解放は無視されます。プロパティに割り当てる必要なく、グローバルキューに直接アクセスできます。

キューをディスパッチするには、同期と非同期の2つの方法があります。

SYNC DISPATCHINGは、キューがディスパッチされたスレッド(呼び出しスレッド)が、キューのディスパッチ後に一時停止し、再開する前にそのキューブロック内のタスクの実行が完了するのを待機することを意味します。同期的にディスパッチするには:

DispatchQueue.global(qos: .default).sync {
    // task goes in here
}

ASYNC DISPATCHINGは、呼び出しスレッドがキューのディスパッチ後も実行を続け、そのキューブロック内のタスクの実行が完了するのを待たないことを意味します。非同期でディスパッチするには:

DispatchQueue.global(qos: .default).async {
    // task goes in here
}

タスクをシリアルで実行するにはシリアルキューを使用する必要があると考えるかもしれませんが、それは正確ではありません。複数のタスクをシリアルで実行するには、シリアルキューを使用する必要がありますが、すべてのタスク(それ自体で分離)はシリアルで実行されます。この例を考えてみましょう:

whichQueueShouldIUse.syncOrAsync {
    for i in 1...10 {
        print(i)
    }
    for i in 1...10 {
        print(i + 100)
    }
    for i in 1...10 {
        print(i + 1000)
    }
}

このキューをどのように構成(シリアルまたはコンカレント)またはディスパッチ(同期または非同期)しても、このタスクは常にシリアルで実行されます。3番目のループは2番目のループの前に実行されることはなく、2番目のループは最初のループの前に実行されることはありません。これは、ディスパッチを使用するすべてのキューに当てはまります。複数のタスクやキューを導入するときに、シリアルと同時実行が実際に役立ちます。

次の2つのキューについて考えてみましょう。1つはシリアル、もう1つは同時です。

let serialQueue = DispatchQueue(label: "serial")
let concurrentQueue = DispatchQueue.global(qos: .default)

2つの同時キューを非同期でディスパッチするとします。

concurrentQueue.async {
    for i in 1...5 {
        print(i)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
101
2
102
103
3
104
4
105
5

それらの出力は(予想どおり)乱雑ですが、各キューが独自のタスクをシリアルに実行したことに注意してください。これは同時実行性の最も基本的な例です。2つのタスクが同じキューのバックグラウンドで同時に実行されます。では、最初のシリアルを作成しましょう:

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

101
1
2
102
3
103
4
104
5
105

最初のキューはシリアルで実行されることになっていますか?それは(そして2番目もそうでした)。バックグラウンドで他に何が起こったとしても、キューには関係ありません。シリアルキューにシリアルで実行するように指示したところ、実際に実行されましたが、タスクは1つだけです。次に、2つのタスクを割り当てます。

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
serialQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
2
3
4
5
101
102
103
104
105

そして、これは最も基本的な(そして唯一可能な)シリアライゼーションの例です-同じキュー内の(メインスレッドへの)バックグラウンドで(順番に)シリアルで実行される2つのタスクです。しかし、それらを2つの個別のシリアルキューにした場合(上記の例ではそれらは同じキューであるため)、それらの出力は再びごちゃ混ぜになります。

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
serialQueue2.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
101
2
102
3
103
4
104
5
105

そして、これは、すべてのキューが相互に同時であると言ったときに私が言ったことです。これらは、同時にタスクを実行する2つのシリアルキューです(別々のキューであるため)。キューは他のキューを知らないか、気にしません。ここで、(同じキューの)2つのシリアルキューに戻り、3つ目のキューを同時に追加します。

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
serialQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 1000)
    }
}

1
2
3
4
5
101
102
103
104
105
1001
1002
1003
1004
1005

それは一種の予期しないことですが、並行キューが実行される前にシリアルキューが完了するのを待機したのはなぜですか?それは並行性ではありません。あなたの遊び場は異なる出力を表示するかもしれませんが、私のものはこれを示しました。そして、これは、私の並行キューの優先度がGCDがタスクをより早く実行するのに十分に高くなかったために、これを示しました。したがって、すべてを同じに保ちながらグローバルキューのQoS(キューの優先度レベルであるサービスの品質)を変更するとlet concurrentQueue = DispatchQueue.global(qos: .userInteractive)、出力は期待どおりになります。

1
1001
1002
1003
2
1004
1005
3
4
5
101
102
103
104
105

2つのシリアルキューはタスクを(期待どおりに)シリアルで実行し、コンカレントキューは高い優先度(高いQoS、またはサービス品質)が与えられたため、タスクをより速く実行しました。

最初の印刷例のように、2つの同時キューは、(予想どおり)乱雑な印刷を示します。それらをシリアルできれいに印刷するには、両方を同じシリアルキュー(同じラベルだけでなく、そのキューの同じインスタンス)にする必要があります。次に、各タスクが他のタスクに対して順次実行されます。ただし、それらをシリアルで印刷させる別の方法は、両方を同時に維持するが、ディスパッチ方法を変更することです。

concurrentQueue.sync {
    for i in 1...5 {
        print(i)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
2
3
4
5
101
102
103
104
105

同期ディスパッチとは、キュー内のタスクが完了するまで呼び出しスレッドが待機してから続行することを意味します。ここでの警告は、明らかに、最初のタスクが完了するまで呼び出しスレッドが凍結されることです。これは、UIの実行方法とは異なる場合があります。

このため、次のことはできません。

DispatchQueue.main.sync { ... }

これは、実行できないキューとディスパッチングメソッドの唯一の可能な組み合わせ、つまりメインキューでの同期ディスパッチです。これは、中かっこ内でタスクを実行するまでメインキューをフリーズするように要求しているためです。フリーズしたメインキューにディスパッチしました。これはデッドロックと呼ばれます。遊び場で実際にそれを見るには:

DispatchQueue.main.sync { // stop the main queue and wait for the following to finish
    print("hello world") // this will never execute on the main queue because we just stopped it
}
// deadlock

最後に言及すべきことは、リソースです。キューにタスクを与えると、GCDはその内部管理プールから使用可能なキューを見つけます。この回答の記述に関する限り、QoSごとに64のキューが利用可能です。それは多くのように思えるかもしれませんが、特にサードパーティのライブラリ、特にデータベースフレームワークによって、すぐに消費される可能性があります。このため、Appleはキュー管理に関する推奨事項を持っています(以下のリンクに記載されています)。1つは:

プライベート並行キューを作成する代わりに、グローバル並行ディスパッチキューの1つにタスクを送信します。シリアルタスクの場合、シリアルキューのターゲットをグローバルコンカレントキューの1つに設定します。 これにより、スレッドを作成する個別のキューの数を最小限に抑えながら、キューのシリアル化された動作を維持できます。

これを行うには、以前に行ったように(まだ可能です)作成するのではなく、次のようなシリアルキューを作成することをお勧めします。

let serialQueue = DispatchQueue(label: "serialQueue", qos: .default, attributes: [], autoreleaseFrequency: .inherit, target: .global(qos: .default))

詳細については、以下をお勧めします。

https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008091-CH1-SW1

https://developer.apple.com/documentation/dispatch/dispatchqueue


7

私はGCDがどのように機能するかについて正しく理解していれば、私はの2種類があると思いますDispatchQueueserialそしてconcurrent、同時に、どのように2つの方法があるDispatchQueueそのタスクをディスパッチするには、割り当てられclosure、1つ目はasync、およびその他のですsync。これらは一緒に、クロージャー(タスク)が実際に実行される方法を決定します。

私はそれを見つけてserialconcurrentキューが使用できるスレッドの数を意味しますserial。1つを意味しますが、concurrent多くを意味します。そして、syncそしてasyncタスクがどのスレッドで実行されますを意味し、呼び出し側のスレッドまたはそのキューの基礎となるスレッドは、sync一方の手段は、呼び出し側のスレッドで実行async手段は、基礎となるスレッドで実行されます。

以下は、Xcodeプレイグラウンドで実行できる実験的なコードです。

PlaygroundPage.current.needsIndefiniteExecution = true
let cq = DispatchQueue(label: "concurrent.queue", attributes: .concurrent)
let cq2 = DispatchQueue(label: "concurent.queue2", attributes: .concurrent)
let sq = DispatchQueue(label: "serial.queue")

func codeFragment() {
  print("code Fragment begin")
  print("Task Thread:\(Thread.current.description)")
  let imgURL = URL(string: "http://stackoverflow.com/questions/24058336/how-do-i-run-asynchronous-callbacks-in-playground")!
  let _ = try! Data(contentsOf: imgURL)
  print("code Fragment completed")
}

func serialQueueSync() { sq.sync { codeFragment() } }
func serialQueueAsync() { sq.async { codeFragment() } }
func concurrentQueueSync() { cq2.sync { codeFragment() } }
func concurrentQueueAsync() { cq2.async { codeFragment() } }

func tasksExecution() {
  (1...5).forEach { (_) in
    /// Using an concurrent queue to simulate concurent task executions.
    cq.async {
      print("Caller Thread:\(Thread.current.description)")
      /// Serial Queue Async, tasks run serially, because only one thread that can be used by serial queue, the underlying thread of serial queue.
      //serialQueueAsync()
      /// Serial Queue Sync, tasks run serially, because only one thread that can be used by serial queue,one by one of the callers' threads.
      //serialQueueSync()
      /// Concurrent Queue Async, tasks run concurrently, because tasks can run on different underlying threads
      //concurrentQueueAsync()
      /// Concurrent Queue Sync, tasks run concurrently, because tasks can run on different callers' thread
      //concurrentQueueSync()
    }
  }
}
tasksExecution()

お役に立てれば幸いです。


7

私はこのメタファーを使用してこれを考えるのが好きです(これは元の画像へのリンクです):

お父さんは助けが必要だ

お父さんが料理をしていて、ソーダを1杯飲んだとしましょう。ガラスをお父さんに持ってきて片付け、片方の皿のそばに置きます。

これでお父さんは自分で料理を全部やっているので、彼は1つずつ料理をしなければなりません。ここであなたのお父さんはシリアルキューを表しています

しかし、あなたはそこに立ってそれが片付けられるのを見ることには本当に興味がありません。グラスを落として部屋に戻ります。これを非同期ディスパッチと呼びます。あなたのお父さんは彼が終わったらあなたに知らせるかもしれないし知らないかもしれませんが重要なビットはあなたがガラスが片付けられるのを待っていないということです。あなたは部屋に戻ってやります

ここで、まだのどが渇いていて、同じグラスにお気に入りの水を入れたいと思っていて、片付けたらすぐに元に戻したいとします。それで、あなたはそこに立って、あなたのお父さんがあなたの料理が終わるまで料理をしているのを見ます。タスクが完了するのを待っている間はブロックされるため、これは同期ディスパッチです。

そして最後に、あなたのお母さんがあなたのお父さんを助けることに決めて、彼と一緒に料理をしているとしましょう。これで、複数の食器を同時に洗浄できるため、キューは並行キューになります。ただし、どのように機能するかに関係なく、そこで待つか、部屋に戻るかを決定できます。

お役に立てれば


3

1.私は、シリアルキューが次々とタスクを実行するために作成および使用されることを読んでいます。ただし、次の場合はどうなりますか?-シリアルキューを作成します。•dispatch_async(作成したばかりのシリアルキュー上)を3回使用して、3つのブロックA、B、Cをディスパッチします。

回答:-3つのブロックすべてが次々に実行されました。理解に役立つサンプルコードを1つ作成しました。

let serialQueue = DispatchQueue(label: "SampleSerialQueue")
//Block first
serialQueue.async {
    for i in 1...10{
        print("Serial - First operation",i)
    }
}

//Block second
serialQueue.async {
    for i in 1...10{
        print("Serial - Second operation",i)
    }
}
//Block Third
serialQueue.async {
    for i in 1...10{
        print("Serial - Third operation",i)
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.