0

我对 iOS 和 Objective-C 开发都很陌生,我的任务是将应用程序从 iOS 6 移植到 iOS 7。一个显而易见的问题是明显的竞争条件导致应用程序锁定并且最终崩溃。应用程序在 iOS7 设备上的成功启动率约为 75%,而在 iOS7 设备上的挂起率约为 25%;我们从未在 iOS6 设备上遇到过这种行为。

使用调试器,我将其范围缩小到“ImageLoader”中似乎存在的问题。我无法确定这是 Apple API 的一部分还是外部库的一部分 - 我无法在上面找到 Apple 文档,但我也无法在任何地方找到它作为库,并且无法使用 ImageLoader是使用 Apple API 的直接结果:[[NSFileManager defaultManager] createFileAtPath:_filePath contents:NULL attributes:NULL];

当锁定发生时,有两个线程访问 ImageLoader。两者都比 ImageLoader::recursiveInitialization() 低了几步。一个卡在 ImageLoader::recursiveSpinLock() 中(特别是 'dyld`OSAtomicCompareAndSwapLongBarrier'),而另一个卡在 'libsystem_kernel.dylib`__psynch_mutexwait:' 中。在我看来,最有可能的是,这是一个未正确实现的互斥锁,可能在内核中。

其中一个线程是主应用程序线程,我可以轻松地浏览该代码。另一个线程始终是应用程序创建的第二个线程,并且不包含应用程序代码;我不确定如何弄清楚它是在哪里或如何创建的。

相关代码:

[[NSFileManager defaultManager] removeItemAtPath:[_filePath stringByAppendingString:tmp] error:nil];
if (![[NSFileManager defaultManager] moveItemAtPath:_filePath toPath:[_filePath stringByAppendingString:tmp] error:nil]) {
    return;
}

NSFileHandle *read = [NSFileHandle fileHandleForReadingAtPath:[_filePath stringByAppendingString:tmp]];
[[NSFileManager defaultManager] createFileAtPath:_filePath contents:NULL attributes:NULL];

简而言之:此代码删除 _filePath + ".tmp" 处的文件,将 _filePath 处的文件移动到 _filePath + ".tmp",获取 _filePath + ".tmp" 上的读取句柄,最后在 _filePath 处创建一个新文件。它锁定在此代码块的最后一行,不再继续前进。这似乎是相当平凡的代码,所以我想知道是否有一些我由于缺乏经验而遗漏的操作系统怪癖。

我已经做了很多搜索,但没有找到其他有类似问题的人。有没有人知道为什么会发生这种情况,或者我可以采取一些步骤来进一步解决这个问题?

编辑:我发现这段代码分派了两个可能同时使用 ImageLoader 的线程。通过修改该代码以在同一个线程上操作(即同步),可以避免此错误。~~

在我看来,这仍然是 ImageLoader 代码中的一个错误——堆栈跟踪表明一个锁定/互斥机制,但显然它不够线程安全。

编辑2:没关系。我只设法降低了频率——这种情况仍在发生,但在删除线程调度代码后“仅”有 10% 的时间。

4

1 回答 1

1

我会尝试在不同的线程上使用不同的 NSFileManager 对象。不要使用 [NSFileManager defaultManager],而是使用 [[NSFileManager alloc] init] 创建单独的实例。看看是否有帮助。文档说除非您使用委托,否则您应该能够在不同的线程上使用相同的实例。但是通过尝试使用不同的实例,它应该有助于验证错误是否发生在您的代码中。

于 2013-11-02T07:51:18.320 回答