web-dev-qa-db-ja.com

ディスパッチキュー、スレッド、NSRunLoopについての説明が必要

次のことが私が知っていることと理解していることです:

グローバルキューはconcurrentキューであり、タスクを複数のスレッドにディスパッチできます。タスクの実行順序は保証されていません。例えば。:

_dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {
 for (int i; i<10; i++) {
  doTask()
 }
})
_

serialキューにディスパッチしたい場合は、

_dispatch_async(dispatch_queue_create("my.serial.queue", nil) {
  ...
}
_

1つのタスクのみがスレッドにディスパッチされ、実行されるたびに。順序はFIFOです。

=====混乱していて、完全に理解していない=======

  1. メインスレッドには、メインスレッドでタスクをループするNSRunLoopがあります。ディスパッチキューと実行ループの関係はどうなっているのでしょうか。タスクをメインスレッドにディスパッチする場合、メインスレッドのNSRunLoopがディスパッチされたタスクを取得して実行するように理解できますか?

  2. 複数のスレッドにタスクをディスパッチするグローバルキューはどうですか? iOS/OSXシステムはスレッドを自動的に作成するだけでなく、スレッドごとにNSRunLoopも作成しますか?そして、各スレッドの実行ループは、グローバルキューからディスパッチされたタスクを取得して実行しますか?

  3. 誰がスレッドを知っていますか? dispatch_async()およびdispatch_sync()関数は、タスクをディスパッチするスレッドを知っていますか、それともqueueはタスクをディスパッチするスレッドを知っていますか?

  4. プログラムでディスパッチキューからスレッド(タスクのディスパッチ先)のNSRunLoopオブジェクトを取得する方法はありますか? (この質問は質問3に関連しています)

23
Leem.fin
  1. メインスレッドの実行ループには、メインキューのキューにあるブロックを実行するステップがあります。実行ループの詳細を理解したい場合、 この答え が役立つかもしれません。

  2. GCDは、並行キューのスレッドを作成します。スレッドで実行中の何かがスレッドの実行ループを最初に要求するまで、スレッドには実行ループがありません。その時点で、システムはスレッドの実行ループを作成します。ただし、実行ループは、そのスレッド上の何かが(-[NSRunLoop run]またはCFRunLoopRunなどを呼び出して)実行するように要求した場合にのみ実行されます。 GCDキュー用に作成されたスレッドを含むほとんどのスレッドには、実行ループはありません。

  3. GCDはスレッドのプールを管理し、ブロックを実行する必要がある場合(キューに追加されたため)、GCDはブロックを実行するスレッドを選択します。 GCDのスレッド選択アルゴリズムは、メインキューに追加されたブロックのメインスレッドを常に選択することを除いて、ほとんどが実装の詳細です。 (GCDは他のキューに追加されたブロックにメインスレッドを使用することもあります。)

  4. メインスレッドの実行ループ(+[NSRunLoop mainRunLoop]またはCFRunLoopGetMainを使用)または現在のスレッドの実行ループ(+[NSRunLoop currentRunLoop]またはCFRunLoopGetCurrentを使用)のみを取得できます。任意のスレッドの実行ループが必要な場合は、そのスレッドでCFRunLoopGetCurrentを呼び出し、その戻り値をスレッド間で安全に同期された方法で渡す方法を見つける必要があります。

    NSRunLoopインターフェースはスレッドセーフではないが、CFRunLoopインターフェーススレッドセーフなので、別のスレッドの実行ループにアクセスする必要がある場合は、CFRunLoopインターフェースを使用する必要があります。

    また、GCDが制御することを期待しているスレッドを拘束しているため、GCDキューで実行されているブロック内で非常に長い間実行ループを実行しないでください。実行ループを長時間実行する必要がある場合は、独自のスレッドを開始する必要があります。この例は CFStream.cの_legacyStreamRunLoop関数 で確認できます。 dispatch_semaphore_tの保護の下で初期化されるsLegacyRLという静的変数で専用スレッドの実行ループを利用できるようにする方法に注意してください。

28
rob mayoff
  1. メインスレッドの実行ループとメインディスパッチキューの関係は、両方がメインスレッドで実行され、メインキューにディスパッチされたブロックがメインスレッドでインターリーブされ、メインランループでイベントが処理されることです。

    同時実行プログラミングガイド が言うように:

    メインディスパッチキューは、アプリケーションのメインスレッドでタスクを実行するグ​​ローバルに利用可能なシリアルキューです。このキューは、アプリケーションの実行ループ(存在する場合)と連携して、キューに入れられたタスクの実行を、実行ループに接続されている他のイベントソースの実行とインターリーブします。アプリケーションのメインスレッドで実行されるため、メインキューは多くの場合、アプリケーションの主要な同期ポイントとして使用されます。

  2. バックグラウンドスレッドにディスパッチするとき、それらのワーカースレッド用にNSRunLoopを作成しません。通常、これらのバックグラウンドスレッドの実行ループも必要ありません。以前は、バックグラウンドスレッド用に独自のNSRunLoopを作成する必要がありましたが(たとえば、バックグラウンドスレッドでNSURLConnectionをスケジュールする場合)、このパターンはそれほど頻繁に必要ではなくなりました。

    歴史的に実行ループを必要とするものについては、バックグラウンドスレッドで実行する方が良いメカニズムがしばしばあります。たとえば、NSURLConnectionではなくNSURLSessionを使用します。または、バックグラウンドスレッドのNSTimerNSRunLoopではなく、GCDタイマーディスパッチソースを作成します。

  3. 誰がスレッドを「知っている」かに関して、キューにディスパッチされたときにワーカースレッドが識別されます。スレッドはキューのプロパティではなく、キューが必要とするときにキューに割り当てられます。

  4. ワーカースレッド用にNSRunLoopを作成したい場合は(とにかく行うべきではありませんが)、作成して自分で追跡します。そして、実行ループでスレッドをスケジュールするとき、GCDのワーカースレッドの1つを占有するのではなく、自分でNSThreadを作成して実行ループをスケジュールする傾向があります。

5
Rob