iPhone 3GS上消耗100%CPU的后台线程导致潜在主线程

iPhone 3GS上消耗100%CPU的后台线程导致潜在主线程,iphone,objective-c,cocoa-touch,multithreading,cpu,Iphone,Objective C,Cocoa Touch,Multithreading,Cpu,在我的应用程序中,我在NSOperationQueue中作为NSInvocationOperations执行10个异步NSURLConnections。为了防止每个操作在连接完成之前返回,我调用CFRunLoopRun(),如下所示: - (void)connectInBackground:(NSURLRequest*)URLRequest { TTURLConnection* connection = [[TTURLConnection alloc] initWithRequest:URLR

在我的应用程序中,我在NSOperationQueue中作为NSInvocationOperations执行10个异步NSURLConnections。为了防止每个操作在连接完成之前返回,我调用CFRunLoopRun(),如下所示:

- (void)connectInBackground:(NSURLRequest*)URLRequest {
 TTURLConnection* connection = [[TTURLConnection alloc] initWithRequest:URLRequest delegate:self];

 // Prevent the thread from exiting while the asynchronous connection completes the work.  Delegate methods will
 // continue the run loop when the connection is finished.
 CFRunLoopRun();

 [connection release];
}
连接完成后,最终连接委托选择器调用CFRunLoopStop(CFRunLoopGetCurrent())以恢复ConnectionBackground()中的执行,从而使其正常返回:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    TTURLConnection* ttConnection = (TTURLConnection*)connection;
    ...
    // Resume execution where CFRunLoopRun() was called.
    CFRunLoopStop(CFRunLoopGetCurrent());
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {  
    TTURLConnection* ttConnection = (TTURLConnection*)connection;
    ...
    // Resume execution where CFRunLoopRun() was called.
 CFRunLoopStop(CFRunLoopGetCurrent());
}
这工作得很好,而且是线程安全的,因为我将每个连接的响应和数据作为实例变量绑定到了TTURLConnection子类中

NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];
NSOperationQueue声称,将其最大并发操作数保留为NSOperationQueueDefaultMaxConcurrentOperationCount允许它动态调整操作数,但在这种情况下,它总是认为1就足够了。因为这不是我想要的,我已经把最大的数字改为10,现在真的很难

问题是这些线程(在SpringBoard和DTMobileIS的帮助下)消耗了所有可用的CPU时间,并导致主线程成为潜在线程。换句话说,一旦CPU的利用率达到100%,主线程就不会像维护平滑的UI那样快速处理UI事件。具体来说,表格视图滚动会变得紧张不安

Process Name  % CPU
SpringBoard   45.1
MyApp         33.8
DTMobileIS    12.2
...
当用户与屏幕交互或表格滚动时,主线程的优先级变为1.0(尽可能高),其运行循环模式变为UIEventTrackingMode。默认情况下,每个操作线程的优先级为0.5,异步连接以NSDefaultRunLoopMode运行。由于我对线程及其运行循环如何基于优先级和模式进行交互的理解有限,我被难住了

在我的应用程序的后台线程中,有没有一种方法可以安全地消耗所有可用的CPU时间,同时仍然保证它的主线程获得它所需要的CPU?也许是通过强制主线程尽可能频繁地运行?(我以为线程优先级会解决这个问题。)

更新12/23: 我终于开始对CPU采样器进行处理,并找到了UI变得不稳定的大部分原因。首先,我的软件调用了一个具有互斥信号量的库。这些锁在短时间内阻塞了主线程,导致滚动稍微跳过

此外,我发现一些昂贵的NSFileManager调用和md5哈希函数花费了太多的时间来运行。过于频繁地分配大对象会在主线程中造成其他一些性能问题

我已经开始解决这些问题,性能已经比以前好多了。我有5个同时连接和滚动是顺利的,但我还有更多的工作要做。我计划写一篇关于如何使用CPU采样器来检测和修复影响主线程性能的问题的指南。感谢到目前为止的评论,他们很有帮助

2010年1月14日更新: 在获得可接受的性能后,我开始意识到CFNetwork框架偶尔会泄漏内存。在CFNetwork内部也随机(但很少)提出异常!我尽我所能避免这些问题,但没有任何效果。我确信这些问题是由于NSURLConnection本身的缺陷造成的。我编写的测试程序除了连接之外什么都不做,它们仍然崩溃和泄漏


最终,我用替换了NSURL连接,崩溃完全停止。CFNetwork几乎从不泄漏,但是,在解析DNS名称时仍然会发生一个非常罕见的泄漏。我现在很满意。希望这些信息能为您节省一些时间

听起来好像是因为某种原因,
NSOperationQueueDefaultMaxConcurrentOperationCount被设置为1!我想你只是让你那可怜的手机超载了。您可能会搞乱线程优先级——我认为Mach内核是可用的,并且是官方认可的API的一部分——但对我来说,这听起来是错误的方法


使用“系统”常量的优点之一是苹果可以为您调整应用程序。您将如何调整此功能以在原始iPhone上运行?10对于未来几年的四核iPhone来说足够高吗?

实际上,您的后台网络线程不能超过两到三个,而且UI也不能保持完全响应


优化用户响应能力,这是用户唯一真正注意到的事情。或者(我真的不想这么说)在你的应用程序中添加一个“Turbo”按钮,它会弹出一个非交互式的模式对话框,并在启动时将并发操作增加到10个。

James,虽然我没有遇到过你的问题,我成功地使用了同步连接在
NSOperation
子类中进行下载

NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];

我使用这种方法从网络位置获取图像资产并更新目标
UIImageView
s。下载发生在
NSOperationQueue
中,更新图像视图的方法在主线程上执行。

我实际上是在为用户而不是设备优化设计。我们的大脑容量不会每两年翻一番,是吗?)。即使这台机器能够同时抓取1000个对象,我也不想这样做。如果我向用户抛出错误数量的内容,他们可能会不知所措,甚至更糟,感到厌烦。如果您消耗了太多的CPU,那么主(UI)线程就会变得匮乏——那么您不是在为用户优化,而是在为网络吞吐量优化。它们是两种截然不同的东西。为用户优化意味着平衡可用资源,但通常意味着“保持UI响应”。这是最主要的规则,用户可以坐在那里