代码之家  ›  专栏  ›  技术社区  ›  James Wald

在iPhone3GS上,后台线程消耗100%的CPU会导致潜在的主线程

  •  10
  • James Wald  · 技术社区  · 15 年前

    在我的应用程序中,我在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())以恢复connectingbackground()中的执行,使其正常返回:

    - (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子类中。

    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内部也随机(但很少)提出异常!我竭尽全力避免这些问题,但没有任何效果。我非常肯定这些问题是由于nsurconnection本身的缺陷造成的。我写了一些测试程序,除了运行nsurconnection之外什么都没有做,它们仍然崩溃和泄漏。

    最终我用 ASIHTTPRequest 撞击完全停止了。CFN网络 几乎 但是,从不泄漏,在解析DNS名称时仍然存在一个非常罕见的泄漏。我现在很满意。希望这些信息能为您节省一些时间!

    3 回复  |  直到 15 年前
        1
  •  7
  •   Kendall Helmstetter Gelner    15 年前

    实际上,您不能拥有两个或三个以上的后台网络线程,并且让UI保持完全响应。

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

        2
  •  3
  •   Stephen Darlington    15 年前

    听起来好像 NSOperationQueueDefaultMaxConcurrentOperationCount 设置为1是有原因的!我觉得你的手机太重了。你可能会把线程优先权搞得一团糟——我认为mach核心是可用的,也是官方祝福的API的一部分——但对我来说,这听起来是错误的方法。

    使用“系统”常量的一个优点是,苹果可以为您调整应用程序。你打算如何调整它以在原始iPhone上运行?10高到足以应付未来几年的四核iPhone吗?

        3
  •  0
  •   ohhorob    15 年前

    James,虽然我没有遇到你的问题,但我成功的是使用同步连接在 NSOperation 子类。

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

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