この問題は、長年にわたって私に悲しみの多くを引き起こしているので、私は経験に苦しんでいる
の利益を共有したいと考え出し:
あなたは(sleep
を呼び出す含む)をブロックの任意の時間を仕事にユニット*をGCDに提出すると、スレッドスターベーションの可能性のある状況が発生しています。さらに、作業単位のブロッキングが同期プリミティブ(すなわちセマフォ、ロックなど)の使用によるものである場合、またはこれらのプリミティブを使用して複数の作業単位がインターロックされている場合、スレッドスターベーションはデッドロックを引き起こす可能性があります。分でよりますが、ここではショートバージョンです:あなたはGCDに提出作業単位でブロックすると
は:
- せいぜい、あなたは非効率的GCDを使用しています。
- 最悪の場合、あなたは の飢餓の可能性、そして(潜在的に)デッドロックの可能性にさらされています。
理由は次のとおりです。OSにプロセスごとのスレッド制限があります。プロセス単位のスレッド制限を変更することは不可能ではありませんが、現実的にはそれほど価値はありません。 GCDには、独自のキュー幅制限(同時キューにサービスするために使用するスレッド数)があります。 (詳細は複雑ですが、ここでは、複数の並行キューを作成すると、一般的にこの制限を回避することはできません)。定義上、GCDの制限はプロセスごとのスレッド制限よりも低くなっています。さらに、GCDのキュー幅の制限は、文書化されていない実装の詳細です。経験的には、OS Xのこの記事の執筆時点では、限界が64であるように見えました。この制限は文書化されていない実装の詳細なので、それが何であるかを知ることはできません。 (パブリックAPIを使用して変更することもできません)理想的に言えば、GCDがキューの幅の制限を1に変更した場合でも、コードは完了するまで実行されるように記述する必要があります。(また、同じスレッドがメインスレッドである場合でも同じことが当てはまるはずですが、それはタスクを不必要に難しくし、少なくとも1つのバックグラウンドスレッドが常に存在すると想定するのは安全です。 GCDを使っていないとGCDを使う価値はほとんどありません)。
どうやってやりますか? I/Oを待つ間にブロックしている場合は、dispatch_io
ファミリーの呼び出しを使用するようにコードを移行することを検討してください。準ビジー待機状態(元の質問)の場合は、一定時間後に何かを確認するためにdispatch_after
を使用できます。それ以外の場合は、ディスパッチタイマーやディスパッチソースが適切かもしれません。
私は、GCDに提出された作業単位で完全にブロックされないようにすることは必ずしも実用的ではないことを認めた最初の人です。さらに、GCDとObjective-Cのブロック構文を組み合わせることで、ブロッキング/同期操作をバックグラウンドスレッドに移動することでUIスレッドをブロックする状況を回避するための、表現が簡単で読みやすいコードを書くことが非常に簡単になります。開発の迅速化の面では、このパターンは非常に役立ち、私はいつもそれを使用しています。つまり、GCDを使用して作業単位をエンキューすると、ブロック化された実装の詳細なことがわかります実際には制御することはできません(パブリックAPIを使用してGCDのキュー幅を設定/変更することはできません)。
忙しい待ち時間sleep
またはusleep
を呼び出す)は、のうちの1つで、が避けられ、は少なくとものGCDに提出された作業単位でブロックできます。私はさらに進んで、が常にであるという大胆な声明を、GCDワークユニットで忙しく待って実行できるあらゆる操作を実装するために、より迅速に開発することができます。
* "作業単位"を使用してObjective-Cブロックや関数ポインタ/引数を参照してGCDに実行依頼して、作業 "ブロック"との混乱を制限しています。あなたのスレッドがカーネルに中断されています "。
通常、スリープループは、再考が必要なデザインの指標とみなされます。ポーリングではなく、準備が整ったときにスレッドに通知することができない理由はありますか? – drekka
@Derek - それはポーリングではないかもしれませんが、例えば、少なくとも50msごとに起こるものです。それは少なくとも私の前提ですが、良い質問です。 :) –
@James Black: 'dispatch_after'(そして友人)がその問題を解決するのにもっと適していますか? –