objective-c - 主队列上的 dispatch_sync 与 dispatch_async

请耐心等待,这需要一些解释。我有一个类似于下面的函数。

上下文:“aProject”是一个名为 LPProject 的核心数据实体,带有一个名为“memberFiles”的数组,其中包含另一个名为 LPFile 的核心数据实体的实例。每个 LPFile 代表磁盘上的一个文件,我们要做的是打开每个文件并解析其文本,寻找指向其他文件的 @import 语句。如果我们找到@import 语句,我们希望找到它们指向的文件,然后通过添加与代表第一个文件的核心数据实体的关系来将该文件“链接”到该文件。由于所有这些都可能在大文件上花费一些时间,因此我们将使用 GCD 在主线程之外完成。

- (void) establishImportLinksForFilesInProject:(LPProject *)aProject {
    dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     for (LPFile *fileToCheck in aProject.memberFiles) {
         if (//Some condition is met) {
            dispatch_async(taskQ, ^{
                // Here, we do the scanning for @import statements. 
                // When we find a valid one, we put the whole path to the imported file into an array called 'verifiedImports'. 

                // go back to the main thread and update the model (Core Data is not thread-safe.)
                dispatch_sync(dispatch_get_main_queue(), ^{

                    NSLog(@"Got to main thread.");

                    for (NSString *import in verifiedImports) {  
                            // Add the relationship to Core Data LPFile entity.
                    }
                });//end block
            });//end block
        }
    }
}

现在,事情变得奇怪了:

此代码有效,但我发现了一个奇怪的问题。如果我在一个有几个文件(大约 20 个)的 LPProject 上运行它,它运行得很好。但是,如果我在具有更多文件(例如 60-70)的 LPProject 上运行它,它确实 NOT 正确运行。我们永远不会回到主线程, NSLog(@"got to main thread"); 永远不会出现并且应用程序挂起。但是,(这就是事情变得非常奇怪的地方)---如果我首先在小项目上运行代码然后在大项目上运行它,一切都会完美运行。只有当我首先在大型项目上运行代码时才会出现问题。

如果我将第二条调度线更改为以下内容,那就是踢球者:

dispatch_async(dispatch_get_main_queue(), ^{

(即使用 async 而不是 sync 将 block 分派(dispatch)到主队列),一切正常。完美。无论项目中有多少文件!

我无法解释这种行为。任何有关下一步测试的帮助或提示将不胜感激。

最佳答案

这是与磁盘 I/O 和 GCD 相关的常见问题。基本上,GCD 可能会为每个文件生成一个线程,并且在某个时间点,您有太多线程让系统无法在合理的时间内提供服务。

每次调用 dispatch_async() 并在该 block 中尝试任何 I/O(例如,看起来您正在此处读取一些文件)时,该代码块所在的线程很可能是执行将在等待从文件系统读取数据时阻塞(被操作系统暂停)。 GCD 的工作方式是,当它看到它的一个工作线程在 I/O 上被阻塞并且你仍然要求它同时做更多的工作时,它只会产生一个新的工作线程。因此,如果您尝试在一个并发队列上打开 50 个文件,您最终可能会导致 GCD 产生约 50 个线程。

对于系统而言,线程过多,无法进行有意义的服务,最终导致主线程无法获得 CPU。

解决此问题的方法是使用串行队列而不是并发队列来执行基于文件的操作。这很容易做到。您需要创建一个串行队列并将其作为 ivar 存储在您的对象中,这样您就不会最终创建多个串行队列。所以删除这个调用:

dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

在你的 init 方法中添加这个:

taskQ = dispatch_queue_create("com.yourcompany.yourMeaningfulLabel", DISPATCH_QUEUE_SERIAL);

在你的 dealloc 方法中添加这个:

dispatch_release(taskQ);

并将其作为 ivar 添加到您的类声明中:

dispatch_queue_t taskQ;

https://stackoverflow.com/questions/6538744/

相关文章:

iphone - AdMob 崩溃并显示 [GADObjectPrivate changeState

ios - ld : file not found: linker command failed w

objective-c - 反转 NSString 文本

objective-c - 最大 CGFloat 值是否有常数?

iphone - HTML 内容适合 UIWebview 而无需缩小

objective-c - 如何在 Swift 中实现这个多行字符串字面量宏?

iphone - 将数据传回前一个 View Controller

objective-c - 将 UIColor 保存到 NSUserDefaults 并从中加载

iphone - 如何将 nil 添加到 nsmutablearray?

ios - command/usr/bin/codedesign 失败,退出代码 1-代码符号错误