GCD串行队列似乎不是串行执行的

ink*_*002 6 queue grand-central-dispatch ios serial-processing

我有一个方法,有时可以在我的代码中调用.下面是一个非常基本的示例,因为代码处理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");
    });
}
Run Code Online (Sandbox Code Playgroud)

我认为每次调用此方法时我都会得到以下输出..."在processImages""使用processImages完成""在processImages""完成了processImages"等...

但我总是得到

"在processImages""在processImages""完成与processImages""完成与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;
}
Run Code Online (Sandbox Code Playgroud)

这个对象只创建一次,据我所知,永远不能再根据我的代码再次创建...我会运行测试以确保.

更新2:我认为发生了什么,如果您同意/不同意,请对此发表评论....

显然我的主要问题是,似乎这个代码块正在同时执行,创建重复的条目(导入相同的照片两次),如果它是串行运行通常不会这样做.处理照片时,会对其应用"脏"位,以确保下次调用该方法时它会跳过此图像,但这不会发生,并且某些图像会被处理两次.这可能是因为我使用enumerategroupswithtypes枚举第二个队列中的对象:在该serialQueue中?

  1. 调用processImages
  2. enumerateObjects
  3. 立即从enumerateObjects返回,因为它本身是异步的
  4. 结束对processImages的调用

虽然由于enumerategroups可能仍在运行,但是队列可能已完成,因为它在枚举组完成工作之前到达块的末尾,所以processImages并没有真正完成.这对我来说似乎有可能吗?

ama*_*ttn 5

串行队列绝对会串行执行。但是,不能保证它们在同一线程上执行。

假设您使用相同的串行队列,问题是 NSLog 不能保证在从不同线程同时调用时以正确的顺序输出结果。

这是一个例子:

  1. SQ 在线程 X 上运行,发送“In processImages”
  2. 日志打印“In proc”
  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");
});
Run Code Online (Sandbox Code Playgroud)

如果所有的 NSlog 调用都在同一个队列中,你就不应该有这个问题。


Nik*_*sov 0

如果问题是“串行队列可以异步执行任务吗?” 那么答案是否定的。如果您认为可以,您应该确保所有任务确实在同一个队列上执行。您可以在块中添加以下行并比较输出:

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

确保您在队列上执行的块中写入 NSLog,而不是在 enumerateGroupsWithTypes:usingBlock:failureBlock 中:您也可以尝试像这样创建队列

dispatch_queue_create("label", DISPATCH_QUEUE_SERIAL);
Run Code Online (Sandbox Code Playgroud)

但我认为这不会改变任何事情

编辑:顺便说一下,方法

enumerateGroupsWithTypes:usingBlock:failureBlock:
Run Code Online (Sandbox Code Playgroud)

是异步的,为什么要在另一个队列上调用呢?

更新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");
});
Run Code Online (Sandbox Code Playgroud)