2
//my_serial_queue is a serial_dispatch_queue

dispatch_async(my_serial_queue, ^{

    //access a shared resource such as a bank account balance
    [self changeBankAccountBalance];

});

それぞれが銀行口座の残高にアクセスして変更する 100 のタスクを送信すると、シリアル キューが各タスクを順番に実行することは理解できますが、dispatch_async を使用すると、これらのタスクも順番に終了しますか?

シリアル キューに非同期で送信したタスク #23 が完了するまでに非常に長い時間がかかる場合はどうすればよいですか? タスク #24 は、タスク #23 が完了したときにのみ開始されますか?それとも、タスク #23 が完了する前にタスク #24 が開始されますか? もしそうなら、タスク #24 がジョブを開始するときに銀行口座の残高が間違っていて、データの整合性が損なわれるのではないでしょうか?

ありがとう!!

4

2 に答える 2

8

はい、専用のシリアル キューは、複数のスレッド間で共有されるリソースへのアクセスを同期する素晴らしい方法です。そして、はい、シリアル キューを使用すると、各タスクは前のタスクが完了するまで待機します。

2 つの観察:

  1. これは非常に非効率的なプロセスのように思えますが、共有リソースの同時更新を最小限に抑えることを目標とする同期手法 (キューベースまたはロックベースのアプローチ) の中心にあるものです。

    NSLockただし、多くのシナリオでは、単純なミューテックス ロック、または@synchronizedディレクティブなどの他の一般的な手法よりも、シリアル キュー手法の方がパフォーマンスが大幅に向上します。代替の同期手法については、 「スレッド プログラミング ガイド」の「同期」セクションを参照してください。ロックの代わりにキューを使用する方法については、同時実行プログラミング ガイドの「スレッドからの移行」セクションの「ロック ベースのコードの削除」を参照してください。

  2. シリアル キュー パターンのバリエーションとして、GCD 同時キューを作成する「リーダー/ライター」パターンを使用する方法があります。

    queue = dispatch_queue_create("identifier", DISPATCH_QUEUE_CONCURRENT);
    

    次に、 を使用して読み取りを実行しますdispatch_syncが、 を使用して書き込みを実行しますdispatch_barrier_async。最終的に効果的なのは、同時読み取り操作を許可することですが、書き込みが同時に実行されないようにすることです。

    リソースで同時読み取りが許可されている場合、リーダー/ライター パターンにより、シリアル キューのパフォーマンスをさらに向上させることができます。

つまり、タスク #24 をタスク #23 まで待機させるのは効率が悪いように見えますが、これは、共有リソースの同時更新を最小限に抑えようとする同期手法に固有のものです。また、GCD シリアル キューは驚くほど効率的なメカニズムであり、多くの単純なロック メカニズムよりも優れていることがよくあります。リーダー/ライター パターンは、状況によっては、パフォーマンスをさらに向上させることができます。


以下の私の最初の回答は、「シリアル ディスパッチ キューは同時実行をどのように保証しますか?」というタイトルの混乱を招く元の質問への回答でした。振り返ってみると、これは間違った用語を誤って使用しただけです。


これは興味深い言葉の選択です。「シリアル ディスパッチ キューはどのようにして並行性を保証するのでしょうか?」

キューには、シリアル、コンカレント、およびメイン キューの3 つのタイプがあります。シリアル キューは、名前が示すように、前のブロックが終了するまで、次にディスパッチされたブロックを開始しません。(あなたの例を使用すると、これは、タスク23に時間がかかる場合、完了するまでタスク24を開始しないことを意味します。)これは重要な場合があります(たとえば、タスク24がタスク23の結果に依存している場合、または両方のタスク23および 24 が同じ共有リソースにアクセスしようとしています)。

これらのさまざまなディスパッチされたタスクを相互に同時に実行したい場合は、並行キューを使用します ( で取得したグローバル並行キューのいずれか、またはオプションを使用しdispatch_get_global_queueて独自の並行キューを作成できます)。同時キューでは、ディスパッチされたタスクの多くが同時に実行される場合があります。同時キューの使用には注意が必要ですが (特に共有リソースの同期)、適切に実装するとパフォーマンスが大幅に向上します。dispatch_queue_createDISPATCH_QUEUE_CONCURRENT

また、これら 2 つのアプローチの妥協点として、操作キューを使用できます。これは、両方を同時に実行できますが、設定することにより、キューで同時に実行される操作の数を制限することもできますmaxConcurrentOperationCount。これを使用する典型的なシナリオは、バックグラウンド ネットワーク タスクを実行する場合で、5 つを超える同時ネットワーク リクエストを必要としない場合です。

詳細については、同時実行プログラミング ガイドを参照してください。

于 2013-09-25T17:13:33.083 に答える
5

man dispatch_queue_create「シリアル キューにディスパッチされたブロックによって実行されるすべてのメモリ書き込みは、同じキューにディスパッチされた後続のブロックから見えることが保証されます。」したがって、シリアル キューは、変更可能な状態へのアクセスをシリアル化して競合状態を回避するための優れた方法です。

dispatch_async を使用すると、これらのタスクも順次終了しますか?

はい。キューは、ブロックをキューに入れる方法ではなく、実行ポリシーを決定します。

つまり、キューがシリアルの場合、async または sync でキューイングしてもそれは変わりません。唯一の違いは、プログラムの残りの実行を続行する前に、このブロックが完了するのを待つべきかということです。dispatch_async=いいえ、dispatch_sync=はい。

シリアル キューに非同期で送信したタスク #23 が完了するまでに非常に長い時間がかかる場合はどうすればよいですか?

何も変わりません。シリアル キューは常に、前にデキューされたブロック (#23) が完了するのを待ってから、次のブロック (#24) をデキューして実行します。キューの停止が懸念される場合は、ブロック コード内にタイムアウトを実装する必要があります。

于 2013-09-25T17:34:45.030 に答える