iPhone-Grand Central Dispatchメインスレッド


145

私は私のアプリで大規模な中央ディスパッチを使用して成功していますが、次のようなものを使用することの本当の利点は何ですか?

dispatch_async(dispatch_get_main_queue(), ^{ ... do stuff

あるいは

dispatch_sync(dispatch_get_main_queue(), ^{ ... do stuff

つまり、どちらの場合も、アプリが実行される場所であるメインスレッドで実行されるブロックを起動しているため、これは負荷の軽減に役立ちません。最初のケースでは、ブロックを実行するタイミングを制御できません。ブロックを発射してから0.5秒後にブロックが実行されるケースを見てきました。2番目のケースは、

[self doStuff];

正しい?

君たちはどう思う?


9
ちなみに、メインキューをdispatch_syncにスローすると、デッドロックが発生します。
Brooks Hanes

5
ドキュメントでそれを読んでください:「dispatch_asyncとは異なり、[dispatch_sync]はブロックが完了するまで戻りません。この関数を呼び出し、現在のキューをターゲットにするとデッドロックが発生します。」...しかし、おそらく私はこれを間違って読んでいます...(現在のキューはメインスレッドを意味しません)。間違えたら訂正してください。
Brooks Hanes、

4
@BrooksHanesは常に正しいとは限りません。これは、デッドロックを発生します場合は、メインスレッド上に既にあります。そうでなければ、デッドロックはありません。こちらを参照してください
Honey

回答:


296

メインキューへのブロックのディスパッチは通常、バックグラウンドキューから行われ、バックグラウンド処理が終了したことを通知します。

- (void)doCalculation
{
    //you can use any string instead "com.mycompany.myqueue"
    dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);

    dispatch_async(backgroundQueue, ^{
        int result = <some really long calculation that takes seconds to complete>;

        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateMyUIWithResult:result];
        });    
    });
}

この場合、バックグラウンドキューで時間のかかる計算を行っており、計算が完了したらUIを更新する必要があります。通常、UIの更新はメインキューから行う必要があるため、2番目にネストされたdispatch_asyncを使用して、メインキューに「シグナル」で返します。

メインキューにディスパッチする例はおそらく他にもありますが、通常はこの方法で行われます。つまり、バックグラウンドキューにディスパッチされたブロック内からネストされます。

  • バックグラウンド処理が完了-> UIを更新
  • バックグラウンドキューで処理されたデータのチャンク->メインキューに次のチャンクを開始するよう通知
  • バックグラウンドキューの受信ネットワークデータ->メッセージが到着したことをメインキューに通知
  • などなど

なぜメインキューからメインキューにディスパッチしたいのかについては、まあ、通常は実行しないでしょう。ただし、実行ループの次の時間に実行する作業をスケジュールするために実行することは考えられます。


ああ、なるほど。だから、私は正しい。すでにメインキューにいる場合、別のキューにいて、UIを更新する場合にのみ、そのようにしてもメリットはありません。ありがとう。
ダック

メインキューからこれを行うのがあまり役に立たない理由について話すために私の回答を編集しました。
ロビンサマーヒル

また、iOS 4にはバグがあると思います(iOS 5ではなくなっている可能性があります)。メインスレッドからメインキューへのdispatch_syncがハングするだけなので、完全に回避します。
joerick

10
これはバグではなく、予想される動作です。確かにそれほど有用な動作ではありませんが、dispatch_syncを使用するときは常にデッドロックに注意する必要があります。システムが常にプログラマーのエラーからあなたを保護することを期待することはできません。
Robin Summerhill、2011年

2
ここでbackgroundQueueとは何ですか?backgroundQueueオブジェクトの作成方法
Nilesh Tupe '11 / 08/11

16

メインスレッドからメインキューにブロックをディスパッチすると便利です。メインキューは、キューに入れられた他のブロックを処理する機会を与え、他のすべてのブロックを実行しないようにします。

たとえば、それでも多くの同時接続を処理する基本的にシングルスレッドのサーバーを作成できます。キューの個々のブロックに時間がかかりすぎない限り、サーバーは新しい要求に応答し続けます。

プログラムが何もせずにすべてのイベントをイベントに応答して過ごす場合、これは非常に自然なことです。メインキューで実行するようにイベントハンドラーを設定してから、dispatch_main()を呼び出すだけで、スレッドの安全性をまったく気にする必要がない場合があります。


11

うまくいけば、dispatch_asyncとdispatch_syncの違いについて疑問に思っているという点であなたの質問を正しく理解していますか?

dispatch_async

ブロックを非同期にキューにディスパッチします。つまり、ブロックをキューに送信し、メソッドの残りのコードの実行を続行する前にブロックが戻るのを待ちません。

dispatch_sync

ブロックをキューに同期的にディスパッチします。これにより、ブロックの実行が完了するまで、メソッド内の残りのコードが実行されなくなります。

私は主にdispatch_asyncバックグラウンドキューを使用して、メインキューから作業を終了し、デバイスにある可能性のある追加のコアを利用しました。次にdispatch_async、UIを更新する必要がある場合はメインスレッドに移動します。

幸運を


1
感謝しますが、メインキューにあるメインキューに何かを送信することの利点について質問します。
ダック

9

これが役立つのは、長い操作の前にスピナーを設定するなどのUIアクティビティの場合です。

- (void) handleDoSomethingButton{

    [mySpinner startAnimating];

    (do something lengthy)
    [mySpinner stopAnimating];
}

長い間メインスレッドをブロックしていて、UIKitが実際にスピナーを開始できないようにしているため、機能しません。

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

     dispatch_async (dispatch_get_main_queue(), ^{
          (do something lengthy)
          [mySpinner stopAnimating];
    });
}

実行ループに制御を戻し、UIの更新をスケジュールしてスピナーを開始し、ディスパッチキューから次のものを取得します。これが実際の処理です。処理が完了すると、アニメーションの停止が呼び出され、実行ループに戻り、UIは停止で更新されます。


@Jerceratopsはい、しかしそれは現在の実行ループが完了することを可能にします。
Dan Rosenstark、2015年

3
はい、しかしそれはまだひどいです。それでもUIはブロックされます。この直後に別のボタンを押すかもしれません。または、スクロールしてみてください。「(何かを長くする)」はメインスレッドでは発生しないはずであり、ボタンに「完了」をクリックさせるdispatch_asyncは許容できるソリューションではありません。
Jerceratops 2015年


1

非同期は非同期を意味し、ほとんどの場合それを使用する必要があります。メインスレッドで同期を呼び出さないでください。タスクが完了するまで、UIがロックされます。あなたはここでこれをSwiftで行うより良い方法です:

runThisInMainThread { () -> Void in
    // Run your code like this:
    self.doStuff()
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

それは私のリポジトリの標準機能として含まれています、それをチェックしてくださいhttps : //github.com/goktugyil/EZSwiftExtensions

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.