1

NSOperation をサブクラス化して少しの作業を完了すると、非常に簡単にデッドロックすることがわかりました。以下に、完了しない理由を非常に簡単に理解できるおもちゃの例を示します。

デッドロックを防止する解決策を、呼び出し先ではなく、呼び出し元の観点から考えているようにしか思えません。たとえば、呼び出し元は、終了を待たずに実行ループを実行し続けることができます。操作中にメインスレッドがメッセージを同期する必要がある場合、操作サブクラスが実装できる標準的なソリューションがあるかどうか疑問に思っていますこのタイプのデッドロックを防ぎます。私は非同期プログラミングに足を踏み入れ始めたばかりです...

@interface ToyOperation : NSOperation

@end

@implementation ToyOperation

- (void)main
{
    // Lots of work

    NSString *string = @"Important Message";
    [self performSelector:@selector(sendMainThreadSensitiveMessage:) onThread:[NSThread mainThread] withObject:string waitUntilDone:YES];

    // Lots more work
}

- (void)sendMainThreadSensitiveMessage:(NSString *)string
{
    // Update the UI or something that requires the main thread...
}

@end

- (int)main
{
    ToyOperation *op = [[ToyOperation alloc] init];
    NSOperationQueue *opQ = [[NSOperationQueue alloc] init];
    [opQ addOperations: @[ op ] waitUntilFinished:YES];    // Deadlock

    return;
}
4

1 に答える 1

3

操作中にメインスレッドがメッセージを同期する必要がある場合、このタイプのデッドロックを防ぐために操作サブクラスが実装できる標準的な解決策があるかどうか疑問に思っています。

がある。 メイン キューへの同期呼び出しを行わないでくださいさらに、メイン キューから同期呼び出しを行わないで ください。 そして、実際には、キューから他のキューへの同期呼び出しを行わないことと要約できます。

そうすることで、メイン キューがブロックされないことが保証されます。確かに、このルールに違反したくなるような例外的なケースが存在する可能性があり、それが本当に、本当に避けられない場合でさえあります。しかし、単一の dispatch_sync() (または NSOpQueue waitUntilDone) でさえデッドロックする可能性があるため、それは例外です。

もちろん、キューからキューへのデータ更新は注意が必要です。いくつかのオプションがあります。同時実行性に安全なデータ層 (非常に難しい)、不変オブジェクトまたはデータのコピーのみを渡す (通常は表示目的でメイン キューに渡す - かなり簡単ですが、潜在的にコストがかかる)、またはモデルのような UUID ベースのフォールトを行うことができます。コアデータが使用します。これをどのように解決するかに関係なく、この問題は他の同時実行モデルと比較して新しいものではありません。

1 つの例外は、ロックをキューに置き換える場合です (たとえば、クラスに対して内部的に @synchronized() を使用する代わりに、シリアル GCD キューを使用し、同期操作を実行する必要があるすべての場所でそのキューに対して dispatch_sync() を使用します。より高速で簡単です)。 .)。しかし、これは例外ではなく、まったく別の問題を解決します。

于 2013-06-29T16:02:31.750 に答える