GCD串行队列似乎没有串行执行(GCD serial queue does not seem to

2019-08-18 00:05发布

我有,有时可以在我的代码中调用的方法。 下面是一个非常简单的例子,如代码处理图像和文件从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");
    });
}

我倒觉得,每次调用此方法时,我会得到下面的输出......“在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;
}

这个对象只创建一次,而据我可以告诉永远无法再创建基于关我的代码...我将进行测试,以确保虽然。

更新2:我认为正在发生的,请在此,如果你同意意见/不同意....

显然,我的主要问题是,它似乎正在被同时执行的代码块,创建重复条目(导入相同的图片两次)时,它通常不会做,如果它连续运行。 如果照片是处理的“脏”的位被施加到它确保下一次方法被调用它跳过该图像中,但是这是不会发生与一些图像被处理两次。 难道这是因为我在列举使用enumerategroupswithtypes第二队列中的对象的事实:即serialQueue内?

  1. 呼叫processImages
  2. enumerateObjects
  3. 立即从enumerateObjects返回,因为它是异步本身
  4. 结束通话到processImages

processImages是不是真的,虽然由于做了这样的事实,enumerategroups可能还在运行,但队列可能的事情,因为它达到enumerategroups之前完成工作块结束它完成。 这似乎是一个可能性,我呢?

Answer 1:

串行队列绝对不会连续进行。 他们不能保证然而相同的线程上执行。

假设你使用的是相同的序列队列中,存在的问题是,从不同的线程几乎同时调用时的NSLog不能保证正确的顺序输出结果。

这里有一个例子:

  1. SQ运行在线程X,将“在processImages”
  2. 日志打印“在PROC”
  3. 上线X SQ,发送“完成与processImages”
  4. SQ运行在线程Y,将“在processImages”
  5. 日志打印 “essImages的\ n”

5.后的NSLog并不一定知道哪个打印,3或4。

如果你确实需要时间排序的记录,您需要登录一个专门的队列。 在实践中,我已经与只使用主队列中没有问题:

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

如果所有的NSLog调用是在同一个队列,你不应该有这个问题。



Answer 2:

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

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

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

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");
});


Answer 3:

enumerateGroupsWithTypes:usingBlock:failureBlock:做它的异步工作在另一个线程,并要求当它这样做(在主线程我认为)传入的块。 从另一个角度看它,如果它完成了所有的同步由当时的方法调用完成后,它可能只是返回组的枚举对象,而不是,例如,对于一个简单的API。

从文档:

此方法是异步的。 当组列举,用户可能被要求确认应用程序的数据访问; 该方法,虽然,立即返回。 你应该执行要在enumerationBlock资产力所能及的工作。

我不知道为什么你尝试使用串行队列来完成的,但如果你只是想阻止同时访问,那么你可以只添加一个变量的地方,跟踪我们是否现在正在列举与否和检查起初,如果你不担心同步问题。 (如果你这样做,也许你应该考虑使用GCD组,但它可能是矫枉过正了这种情况。)



Answer 4:

我打一个问题是这样,对我来说,答案是认识到,从序列化队列的方法异步调用,去到另一个队列处理 - 一个不序列化。

所以,你必须包装有明确的主要方法内的所有来电dispatch_async(serializedQueue, ^{})以确保一切都在正确的顺序完成...



Answer 5:

你可能有多个对象,每个都有自己的串行队列。 分派到任何一个串行队列任务顺序执行,但分派到不同的串行队列的任务将完全交错。

另一个简单的错误是创建不是串行队列,但并发队列...



文章来源: GCD serial queue does not seem to execute serially