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

Jam*_*ald 10 iphone cpu multithreading cocoa-touch objective-c

在我的应用程序中,我在NSOperationQueue中执行10个异步NSURLConnections作为NSInvocationOperations.为了防止每个操作在连接有机会完成之前返回,我调用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];
}
Run Code Online (Sandbox Code Playgroud)

连接完成后,最终连接委托选择器调用CFRunLoopStop(CFRunLoopGetCurrent())以恢复connectInBackground()中的执行,允许它正常返回:

- (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());
}
Run Code Online (Sandbox Code Playgroud)

这很好用,它是线程安全的,因为我将每个连接的响应和数据作为实例变量捆绑在TTURLConnection子类中.

NSOperationQueue声称将其最大并发操作数保留为NSOperationQueueDefaultMaxConcurrentOperationCount允许它动态调整操作数,但是,在这种情况下,它总是决定1就足够了.由于这不是我想要的,我已将最大数量更改为10并且现在严重拖延.

这个问题是这些线程(在SpringBoard和DTMobileIS的帮助下)占用了所有可用的CPU时间并导致主线程变得潜伏.换句话说,一旦CPU 100%被利用,主线程就不会像它需要的那样快地处理UI事件,以便维持平滑的UI.具体而言,表格视图滚动变得紧张.

Process Name  % CPU
SpringBoard   45.1
MyApp         33.8
DTMobileIS    12.2
...
Run Code Online (Sandbox Code Playgroud)

当用户与屏幕交互或者表滚动时,主线程的优先级变为1.0(最高可能),其运行循环模式变为UIEventTrackingMode.默认情况下,每个操作的线程都是0.5优先级,异步连接在NSDefaultRunLoopMode中运行.由于我对线程及其运行循环如何根据优先级和模式进行交互的理解有限,我很难过.

有没有办法安全地消耗我的应用程序的后台线程中的所有可用CPU时间,同时仍然保证其主线程被给予尽可能多的CPU?也许通过强制主线程尽可能多地运行?(我认为线程优先级会照顾到这一点.)

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

另外,我发现了一些昂贵的NSFileManager调用和md5散列函数,这些函数花费了太多时间来运行.过于频繁地分配大对象会在主线程中引起一些其他性能命中.

我已经开始解决这些问题了,性能已经比以前好多了.我有5个同时连接,滚动很顺利,但我还有更多的工作要做.我打算编写一个指南,介绍如何使用CPU Sampler来检测和修复影响主线程性能的问题.感谢您的评论到目前为止,他们很有帮助!

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

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

Ken*_*ner 7

在实践中,您不能拥有超过两个或三个后台网络线程,并且UI保持完全响应.

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