11

私は、メインプロジェクトでそれらを使用する前に、その仮定NSOperationをテストするテストプロジェクトを作成しました。NSOperationQueue

私のコードは非常に単純なので、ここにすべてを含めます。これは、ARCがオンになっているコマンドラインFoundationプロジェクトを使用しています。

Operation.h

#import <Foundation/Foundation.h>

@interface Operation : NSOperation

@property (readwrite, strong) NSString *label;

- (id)initWithLabel: (NSString *)label;

@end

Operation.m

#import "Operation.h"

@implementation Operation

- (void)main
{
    NSLog( @"Operation %@", _label);
}

- (id)initWithLabel: (NSString *)label
{
    if (( self = [super init] )) {
        _label = label;
    }
    return self;
}

@end

Main.m

#import <Foundation/Foundation.h>
#import "Operation.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        queue.maxConcurrentOperationCount = 1;

        id create = [[Operation alloc] initWithLabel: @"create"];
        id update1 = [[Operation alloc] initWithLabel: @"update1"];
        id update2 = [[Operation alloc] initWithLabel: @"update2"];

        [update1 addDependency: create];
        [update2 addDependency: create];

        [queue addOperation: update1];
        [queue addOperation: create];
        [queue addOperation: update2];

        [queue waitUntilAllOperationsAreFinished];

    }
    return 0;
}

私の出力は次のようになります。

2012-05-02 11:37:08.573 QueueTest[1574:1b03] Operation create
2012-05-02 11:37:08.584 QueueTest[1574:1903] Operation update2
2012-05-02 11:37:08.586 QueueTest[1574:1b03] Operation update1

これを書き、いくつかの組み合わせを試してみたところ、次のようにキューの設定を並べ替えると、次のようになりました。

    [queue addOperation: update1];
    [queue addOperation: create];
    [queue addOperation: update2];

    [update1 addDependency: create];
    [update2 addDependency: create];

    [queue waitUntilAllOperationsAreFinished];

私は同じ出力を得ました:

2012-05-02 11:38:23.965 QueueTest[1591:1b03] Operation create
2012-05-02 11:38:23.975 QueueTest[1591:1b03] Operation update1
2012-05-02 11:38:23.978 QueueTest[1591:1903] Operation update2

一部の実行で、update2がupdate1の前に実行されることがわかりましたが、その動作は驚くべきことではありません。NSOperationQueue私がそうするように頼んでいないのに、なぜ決定論的でなければならないのですか?

驚いたのは、依存関係が追加される前にすべてがキューに追加されたとしても、どういうわけかcreateはupdate1とupdate2の前に常に実行されるということです。

明らかに、これはばかげたことですが、私は疑問に思いました。キューに操作を追加してから実行されるまでの遅延は文書化されていますか、それとも何らかの形で予測可能ですか?NSOperationQueue追加された操作の処理を開始するのはいつですか?

本当に、最も重要なのは、正確に何が待っているのか、そしていつその待機が私を防御できないNSOperationQueue何らかの方法で私を噛むのでしょうか?

4

2 に答える 2

17

明らかに、これはばかげたことですが、私は疑問に思いました。キューに操作を追加してから実行されるまでの遅延は文書化されていますか、それとも何らかの形で予測可能ですか?NSOperationQueueはいつ追加された操作の処理を開始しますか?

後:

  • 操作をキューに追加し
  • 操作のすべての依存関係が終了し
  • キューには(優先度の点で)これ以上のことはありません。

依存関係が満たされていない操作の場合、これはすぐに意味する可能性があります。

さらに実験を行うと、NSOperationQueueは、[queue waitUntilAllOperationsAreFinished]、[NSThread sleepForTimeInterval:0.000001]、または中断されたスレッドを介して、現在のスレッドが制御を取得するとすぐに操作の実行を開始することが示されているようです。

この仮定は、2つの理由から偽物です。

  1. これまでのすべてのバージョンのOSX(およびすべてのバージョンのiOS)で実行されるプリエンプティブマルチタスクは、スケジューラーがいつでもスレッドに割り込むことができることを意味します。コントロールを失うためにコントロールを譲る必要はありません。
  2. 現在のすべてのOSXおよびiOSデバイスにある複数のコアは、動作が文字通りaddOperation:戻る前に開始される可能性があることを意味します(ただし、この極端な場合は、単にオーバーヘッドが原因である可能性は低いです)。さらに重要なことに、他の操作は最初の操作が実行されている間に実行できる可能性があります。

これらの点の両方、特に後者は、「順序」の問題がほとんどナンセンスに還元されることも意味します。依存関係ツリーに配置した操作は、そのツリーの下に順番に開始されますが、それ以外の場合はシーケンスがまったくありません。それらの間に依存関係のない操作は、次々に実行されるのではなく、同時に実行されると想定する必要があります。

操作が開始されると、それに依存関係を追加しても効果はありません。これはキューに追加するとすぐに発生する可能性があるため、依存関係を確実に有効にする場合は、操作をキューに追加するに、操作に依存関係を追加する必要があります。

于 2012-05-02T21:04:39.953 に答える
1

さらに実験を行うと、NSOperationQueueは、、、、または中断されたスレッドを介して[queue waitUntilAllOperationsAreFinished]、現在のスレッドが制御を取得するとすぐに操作の実行を開始することが示されているようです。[NSThread sleepForTimeInterval: 0.000001]

(誰かがより良い答えを持っているなら、私はそれを聞いてみたいです、そして代わりにそれを受け入れます。)

于 2012-05-02T20:03:23.507 に答える