シリアルキューのdispatch_asyncとdispatch_syncの違いは?


125

私はこのようなシリアルキューを作成しました:

    dispatch_queue_t _serialQueue = dispatch_queue_create("com.example.name", DISPATCH_QUEUE_SERIAL);

dispatch_asyncこのように呼ばれるの違いは何ですか

 dispatch_async(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_async(_serialQueue, ^{ /* TASK 2 */ });

そしてdispatch_sync、このシリアルキューでこのように呼ばれましたか?

 dispatch_sync(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_sync(_serialQueue, ^{ /* TASK 2 */ });

私の理解では、使用されるディスパッチ方法に関係なく、のTASK 1TASK 2に実行および完了されます。

回答:


409

はい。シリアルキューを使用すると、タスクのシリアル実行が保証されます。唯一の違いはdispatch_sync、ブロックが完了してから戻るだけであるのに対し、ブロックがdispatch_asyncキューに追加されて完了しない場合があるということです。

このコードの

dispatch_async(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_async(_serialQueue, ^{ printf("3"); });
printf("4");

これは、印刷することができる2413か、2143または1234が、1常に前に3

このコードの

dispatch_sync(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_sync(_serialQueue, ^{ printf("3"); });
printf("4");

常に印刷する 1234


注:最初のコードでは、印刷されません1324。そのためprintf("3")、ISが送出した後に printf("2")実行されます。また、タスクはディスパッチされたにのみ実行できます。


タスクの実行時間は何も変更しません。このコードは常に印刷されます12

dispatch_async(_serialQueue, ^{ sleep(1000);printf("1"); });
dispatch_async(_serialQueue, ^{ printf("2"); });

何が起こったか

  • スレッド1:dispatch_async時間のかかるタスク(タスク1)をシリアルキューに送信する
  • スレッド2:タスク1の実行を開始する
  • スレッド1:シリアルキューへの別のタスク(タスク2)のdispatch_async
  • スレッド2:タスク1が終了しました。タスク2の実行を開始する
  • スレッド2:タスク2が終了しました。

そして、あなたはいつも見ます 12


7
2134および1243も印刷できます
Matteo Gobbi

私の質問は、なぜ私たちが通常の方法でそれをしなかったのですか?printf("1");printf("2") ;printf("3") ;printf("4")-との比較dispatch_sync
androniennn 2014

@androniennnは2番目の例ですか?他のスレッドがdispatch_sync(_serialQueue, ^{ /*change shared data*/ });同時に実行されている可能性があるためです。
ブライアンチェン

1
@ asma22複数のスレッド/ディスパッチキュー間で非スレッドセーフオブジェクトを共有すると非常に便利です。シリアルキュー内のオブジェクトにのみアクセスする場合、安全にアクセスしていることがわかります。
ブライアンチェン

1
私はシリアル実行を意味します。すべてのタスクが同じキュー内の他のタスクに関連して逐次実行されるという観点から。原因としては、他のキューとの同時性が考えられます。タスクを同時にディスパッチして実行できることがGCDの要点です。
ブライアンチェン

19

違いdispatch_syncとはdispatch_async簡単です。

どちらの例でも、TASK 1TASK 2にディスパッチされたため、常に前に実行されます。

dispatch_syncただし、この例では、がディスパッチおよび実行されるTASK 2までディスパッチしません。これは「ブロッキング」と呼ばれます。コードは、タスクが実行されるまで待機(または「ブロック」)します。TASK 1

このdispatch_async例では、コードは実行が完了するまで待機しません。両方のブロックがキューにディスパッチ(およびエンキュー)され、残りのコードはそのスレッドで実行を継続します。その後、(キューに他にディスパッチされたものに応じて)将来のある時点でTask 1実行され、実行されますTask 2


2
注文が間違っていると思います。最初の例はasync、ノンブロッキングバージョンです
ブライアンチェン

私はあなたが何を意味していると思うかに対するあなたの答えを編集しました。そうでない場合は、変更して明確にしてください。
JRG-Developer

1
同じキューでdispatch_syncを呼び出してから、dispatch_asyncを呼び出すとどうなりますか?(およびその逆)
0xSina 2013年

1
シリアルキューでは、両方のタスクが引き続き1つずつ実行されます。最初のケースでは、呼び出し元は最初のブロックが終了するまで待機しますが、2番目のブロックを待機しません。2番目のケースでは、呼び出し元は最初のブロックが完了するのを待たずに、2番目のブロックを待ちます。しかし、キューはブロックを順番に実行するので、呼び出し元は両方が完了するまで実質的に待機します。
gnasher729 2014年

1
ブロックは、独自のキューでdispatch_asyncを実行することもできます(後で実行されるブロックをさらに追加します)。独自のシリアルキューまたはメインキューのdispatch_syncはデッドロックします。この状況では、呼び出し元は元のブロックが完了するまで待機しますが、他のブロックは待機しません。注意してください。dispatch_syncはブロックをキューの最後に置き、キューはそのブロックが完了するまでコードを実行してから、dispatch_syncが戻ります。dispatch_asyncは、キューの最後にブロックを追加するだけです。
gnasher729 2014年

5

すべてメインキューに関連しています。4つの順列があります。

i)シリアルキュー、ディスパッチasync:ここではタスクが次々に実行されますが、メインスレッド(UIへの影響)は戻りを待機しません

ii)シリアルキュー、ディスパッチ同期:タスクは次々に実行されますが、メインスレッド(UIへの影響)は遅延を示します

iii)並行キュー、ディスパッチ非同期:ここでタスクは並列に実行され、メインスレッド(UIへの影響)は戻りを待たず、スムーズになります。

iv)並行キュー、ディスパッチ同期:ここではタスクは並列に実行されますが、メインスレッド(UIへの影響)は遅延を示します

並行キューまたはシリアルキューの選択は、前のタスクからの出力が次のタスクに必要かどうかによって異なります。前のタスクに依存している場合は、シリアルキューを採用するか、同時キューを採用します。

そして最後に、これは私たちのビジネスが終わったときにメインスレッドに戻る方法です:

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