7

コード全体で呼び出すことができるメソッドがあります。以下は非常に基本的な例です。コードは iPhone フォト ギャラリーから画像とファイルを処理し、メソッドの実行が完了すると処理済みとしてマークします。

@property (nonatomic, assign) dispatch_queue_t serialQueue;

....

-(void)processImages
{
    dispatch_async(self.serialQueue, ^{
        //block to process images
        NSLog(@"In processImages");

        ....

        NSLog(@"Done with processImages");
    });
}

このメソッドが呼び出されるたびに、以下の出力が得られると思います...「In processImages」「Done with processImages」「In processImages」「Done with processImages」など...

しかし、私はいつも得ます

「In processImages」「In processImages」「Done with processImages」「Done with processImages」など...

シリアル キューは、最初のブロックが完了するまで待ってから開始すると考えていました。私には、メソッドを開始しているように見えますが、最初の呼び出しが終了する前に再度呼び出されて起動し、実際に連続して実行された場合、メソッドはそれらを知っているという事実のために通常は処理されない画像の複製を作成しますすでに処理されていました。たぶん、シリアル キューに関する私の理解は具体的ではありません。入力はありますか?ありがとうございました。

編集:以下のコンテキスト、これはブロックで起こっていることです...これが問題を引き起こす可能性がありますか???

@property (nonatomic, assign) dispatch_queue_t serialQueue;

....

-(void)processImages
{
    dispatch_async(self.serialQueue, ^{
        //library is a reference to ALAssetsLibrary object 

        [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop)
        {
            [group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop)
            {
             ....
             //Process the photos here
            }];
        failureBlock:^(NSError *error) { NSLog(@"Error loading images from library");
        }];

    });
}

-(id)init
{
    self = [super init];
    if(self)
    {
        _serialQueue = dispatch_queue_create("com.image.queue",NULL);
    }
    return self;
}

このオブジェクトは一度だけ作成され、コードに基づいて二度と作成できないと私が知る限り...テストを実行して確認します。

更新 2: 何が起こっていると思いますか。同意/同意しない場合は、これについてコメントしてください....

明らかに私の主な問題は、このコードブロックが同時に実行されているように見え、シリアルで実行された場合に通常はこれを行わないときに、重複したエントリ (同じ写真を 2 回インポートする) を作成することです。写真が処理されると、「ダーティ」ビットが適用され、次にメソッドが呼び出されたときにこの画像がスキップされるようになりますが、これは行われず、一部の画像は 2 回処理されます。これは、その serialQueue 内で enumerategroupswithtypes: を使用して 2 番目のキューでオブジェクトを列挙していることが原因でしょうか?

  1. processImages を呼び出す
  2. enumerateObjects
  3. それ自体が非同期であるため、 enumerateObjects からすぐに戻ります
  4. processImages の呼び出しを終了する

enumerategroups はおそらくまだ実行されているため、processImages は実際には完了していませんが、enumerategroups が動作を完了する前にブロックの最後に到達するため、キューが完了したと見なされる可能性があります。これは私には可能性のように思えますか?

4

7 に答える 7

5

シリアル キューは絶対にシリアルに実行されます。ただし、同じスレッドで実行されることは保証されていません。

同じシリアル キューを使用していると仮定すると、問題は、異なるスレッドからほぼ同時に呼び出されたときに、NSLog が適切な順序で結果を出力することが保証されないことです。

以下に例を示します。

  1. SQ はスレッド X で実行され、「In processImages」を送信します
  2. ログは「処理中」を出力します
  3. スレッド X の SQ は、「Done with processImages」を送信します
  4. SQ はスレッド Y で実行され、「In processImages」を送信します
  5. ログ出力 "essImages\n"

5. 以降、NSLog は必ずしも 3. または 4. のどちらを出力するかを認識していません。

時間順のロギングが絶対に必要な場合は、ロギング専用のキューが必要です。実際には、メイン キューを使用するだけで問題はありませんでした。

dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"whatever");
});

すべての NSlog 呼び出しが同じキューにある場合、この問題は発生しません。

于 2013-03-25T17:01:59.743 に答える
1

enumerateGroupsWithTypes:usingBlock:failureBlock:別のスレッドで非同期に作業を行い、完了時に渡されたブロックを呼び出します(メインスレッドで)。別の観点から見ると、メソッド呼び出しが完了するまでにすべてが同期的に完了した場合、たとえば、より単純な API の代わりに、グループの列挙子オブジェクトを返すことができます。

ドキュメントから:

このメソッドは非同期です。グループが列挙されると、ユーザーはデータへのアプリケーションのアクセスを確認するよう求められる場合があります。ただし、メソッドはすぐに戻ります。enumerationBlock のアセットを使用して、必要な作業を実行する必要があります。

シリアル キューを使用して達成しようとしている理由はわかりませんが、同時アクセスを防ぎたい場合は、現在列挙しているかどうかを追跡する変数をどこかに追加して確認することができます同期の問題について心配する必要がない場合は、最初にそれを行います。(もしそうなら、おそらく GCD グループの使用を検討する必要がありますが、この状況ではおそらくやり過ぎです。)

于 2013-03-28T23:41:37.960 に答える
0

質問が「シリアル キューはタスクを非同期的に実行できますか?」答えはノーです。可能だと思う場合は、すべてのタスクが実際に同じキューで実行されていることを確認する必要があります。ブロックに次の行を追加して、出力を比較できます。

dispatch_async(self.serialQueue, ^{
    NSLog(@"current queue:%p current thread:%@",dispatch_get_current_queue(),[NSThread currentThread]);

enumerateGroupsWithTypes:usingBlock:failureBlock ではなく、キューで実行するブロックに NSLog を書き込むようにしてください。また、このようにキューを作成することもできます。

dispatch_queue_create("label", DISPATCH_QUEUE_SERIAL);

でもそれで何も変わらないと思う

編集:ところで、方法

enumerateGroupsWithTypes:usingBlock:failureBlock:

は非同期ですが、なぜ別のキューで呼び出すのですか?

更新 2: 次のような提案ができます。

dispatch_async(queue, ^{
    NSLog(@"queue");

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER, *pmutex = &mutex;
    pthread_mutex_lock(pmutex);

    ALAssetsLibraryGroupsEnumerationResultsBlock listGroupBlock = ^(ALAssetsGroup *group, BOOL *stop) {
        NSLog(@"block");
        if (group) {
            [groups addObject:group];
        } else {

            [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
            dispatch_async(dispatch_get_current_queue(), ^{
                pthread_mutex_unlock(pmutex);
            });
        }
        NSLog(@"block end");
    };

    [assetsLibrary enumerateGroupsWithTypes:groupTypes usingBlock:listGroupBlock failureBlock:failureBlock];
    pthread_mutex_lock(pmutex);
    pthread_mutex_unlock(pmutex);
    pthread_mutex_destroy(pmutex);
    NSLog(@"queue end");
});
于 2013-03-25T16:50:03.190 に答える
-2

複数のオブジェクトがあり、それぞれに独自のシリアル キューがある場合があります。単一のシリアル キューにディスパッチされたタスクは順次実行されますが、異なるシリアル キューにディスパッチされたタスクは完全にインターリーブされます。

もう 1 つの単純なバグは、シリアル キューではなく、同時キューを作成することです...

于 2013-11-21T16:58:53.597 に答える