これまでのところ、GCD のドキュメントを確認しましたが、dispatch_cancel() が欠落しているようです。これを使用して、すべてのディスパッチのブロック呼び出しをキャンセルしたいと考えています。dispatch_cancel() を実装する方法はありますか?
3 に答える
@HampusNilssonが言及しているように、本質的にリソースをリークし、プロセスを不確定な状態のままにするため、ガベージ収集されていない環境(このような)で実行中の操作を合理的にキャンセルすることはできません。NSOperationQueue
にはキャンセル API があり、その API を使用して実行中の操作のキャンセルを実装できます。ただし、操作自体が協力してフラグをチェックし、クリーンアップして早期に戻る場合に限ります。それは真のハードアボートではありません。
キューに入れられたが開始されていない作業項目のキャンセルに関しては、はい、NSOperationQueue
これを処理しますが、それには追加の費用がかかり、NSOperationQueue
より高いレベルの抽象化になります。GCD のパフォーマンスは、主にロックフリー キューの内部使用に基づいています。ロックフリー キューは、ロックベースの実装よりも高速になりますが、その速度を達成するには、特定のトレードオフが必要になります。たとえば、キャンセルされた操作を削除するためにロックを解放する方法でキューを任意に変更することは、はるかに難しいと思います。公開されたキュー操作を「エンキューのみ」に制限し、作業項目自体 (ブロックと関数 ptr) を不変にすることで、GCD のオーバーヘッドが非常に少なく、パフォーマンスが優れていることを可能にする多くの最適化への扉が開かれたのではないかと思います。
FWIW、一般的なケースでは、操作をキャンセル可能にすることは、既存の GCD API の上に実装するのは非常に簡単なので、この機能を必要とする人は誰でも簡単に自分で行うことができます (そして、特定のニーズにより適した方法で可能性が高いです)。一般化された API よりも)。次の関数を考えてみましょう。これはブロックをキューに入れ、後で呼び出してキューに入れられた操作をキャンセルできるブロックを返します。
dispatch_block_t dispatch_cancelable_async(dispatch_queue_t q, dispatch_block_t b)
{
__block uintptr_t isCancelled = 0;
dispatch_async(q, ^{
if (!isCancelled) b();
});
return [[^{ isCancelled = 1; } copy] autorelease];
}
これはすべての場合に適切なキャンセル方法ではありませんが、最初の概算としては適切です。
「仕事を成し遂げる最高レベルの抽象化を使用してください。」キャンセルが必要で、NSOperationQueue
と GCD の間のオーバーヘッドの差が重要な要素ではない場合は、単に を使用する必要がありますNSOperationQueue
。NSOperationQueue
Objective-C で作業する場合は、using がより慣用的な選択であると主張する人さえいます。それを超えて、GCD の上に一般的なケースの非中止キャンセルを実装することは、示されているように、かなり簡単です。
これらすべてに基づいて、API にキャンセルを組み込むことは、パフォーマンスと複雑さの点で価値のあるトレードオフではなかったのではないかと私は疑っています。
GCD はキャンセル API を実装していません。安全ではないためです (処理中にスレッドが中止される可能性があります)。タスクをキャンセルしたい場合は、「キャンセルされた」ブール値を実装し、タスクの開始時および後で定期的にチェックすることで、自分で行う必要があります。