CFRunLoopPerformBlock与dispatch_async

时间:2012-10-13 09:21:02

标签: ios multithreading grand-central-dispatch

我在后台线程上有一些计算工作,之后我需要更新一些calayer的转换,我试试用

dispatch_async(dispatch_get_main_queue(), ^{calayer.transform = newTransform});

CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^(void) {calayer.transform = newTransform});

我只是觉得他们是一样的, 但是当我使用dispatch_async时,我发现calayer工作得很顺利(可能?)。 这两个功能有什么不同?

4 个答案:

答案 0 :(得分:13)

这里的主要区别是CFRunLoopPerformBlock允许您指定执行块的特定运行循环模式,而dispatch_async(dispatch_get_main_queue(),...)仅在常见模式下执行。也许更适合您所看到的性能问题,CFRunLoopPerformBlock 唤醒主线程。来自CFRunLoopPerformBlock的文档:

  

此方法仅将块排入队列,不会自动唤醒   指定的运行循环。因此,执行块发生了   下次运行循环唤醒以处理另一个输入源。如果你   想要立即完成工作,你必须明确地唤醒它   线程使用CFRunLoopWakeUp函数。

实际上,这通常意味着你的块不会被执行,直到运行循环唤醒(即用户事件发生,定时器触发,运行循环源触发,接收到mach消息等)GCD不是,通过设计,基于运行循环的API;主队列和主线程运行循环之间的关系实际上是一个实现细节。我希望实现唤醒运行循环本身,如果主队列需要服务那么。

如果没有相反的信息,我强烈怀疑这是性能差异的根源。如果您在致电CFRunLoopWakeUp后立即致电CFRunLoopPerformBlock,我预计效果会相似。

答案 1 :(得分:3)

GCD的主队列是一个串行队列。因此,它一次只能运行一个任务。即使该任务运行内部运行循环 - 例如,运行模式对话框 - 然后提交到主队列的其他任务也无法运行,直到完成。

使用CFRunLoopPerformBlock()提交的任务可以在其中一个目标模式下运行运行循环时运行。这包括运行循环是否在使用CFRunLoopPerformBlock()提交的任务中运行。

请考虑以下示例:

CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
    printf("outer task milestone 1\n");
    CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
        printf("inner task\n");
    });
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
    printf("outer task milestone 2\n");
});

产生如下输出:

outer task milestone 1
inner task
outer task milestone 2

虽然这个:

dispatch_async(dispatch_get_main_queue(), ^{
    printf("outer task milestone 1\n");
    dispatch_async(dispatch_get_main_queue(), ^{
        printf("inner task\n");
    });
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
    printf("outer task milestone 2\n");
});

产生

outer task milestone 1
outer task milestone 2
inner task

答案 2 :(得分:1)

我有时会一起使用它们:

dispatch_async(dispatch_get_main_queue(), ^(void) {
    CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopDefaultMode, ^{
        // stuff
    });
});

我使用它将一个块发送到主线程,该主线程将执行而不会导致'故障'当UIScrollview滚动时。

我最近也在使用:

CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopDefaultMode, ^{
    // stuff
});

代替:

[self performSelector:@selector(myMethod:) withObject:myObject afterDelay:0]

为了将代码的执行推迟到下一次通过runloop传递。这样我就不必创建一个特殊的方法来包含我想要执行的代码,也不必将所有要执行的参数包装到单个(id)myObject中。

答案 3 :(得分:0)

可能导致严重麻烦的一种微妙之处是由于“发送”到CFRunLoopPerformBlock的CF块中存在深层错误,并非总是按照它们发送的顺序来调用。如果订单对您的程序很重要,那么您需要找到一种自行提供订单的方法。因此,CFRunLoopPerformBlock的文档以及另一个答案中提到的嵌套都不应使用队列术语。不幸的是,此错误也悄悄传播到NSRunLoop的某些方法中(因为它们当然会调用CFRunLoopPerformBlock)。