2009-12-21 83 views
10

在我的應用程序中,我在NSOperationQueue內執行了10個異步NSURLConnections作爲NSInvocationOperations。爲了防止在連接之前返回每個操作有機會完成我打電話CFRunLoopRun()爲在這裏看到:在iPhone 3GS上消耗100%CPU的後臺線程導致潛在主線程

- (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())恢復在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()); 
} 

這工作得很好,它是線程安全的,因爲我捆綁每個連接的響應,並作爲TTURLConnection子類的實例變量的數據。

NSOperationQueue聲稱,將其最大數量的併發操作保留爲NSOperationQueueDefaultMaxConcurrentOperationCount允許它動態調整操作的數量,但在這種情況下,它總是會決定1是足夠的。由於這不是我想要的,所以我已將最大數目更改爲10,現在它嚴重拖累。

問題是這些線程(在SpringBoard和DTMobileIS的幫助下)消耗了所有可用的CPU時間,並導致主線程變得潛伏。換句話說,一旦CPU被100%利用,主線程就不會像處理UI事件那樣快速地處理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採樣器來檢測和修復影響主線程性能的問題的指南。感謝迄今的評論,他們很有幫助!

UPDATE 1/14/2010: 在達到可接受的性能後,我開始意識到CFNetwork框架偶爾會泄漏內存。CFNetwork內部也隨機提供異常(但很少)!我盡我所能來避免這些問題,但沒有奏效。我很確定這些問題是由NSURLConnection本身的缺陷造成的。我寫了測試程序,除了練習NSURLConnection之外什麼也沒做,他們仍然崩潰和泄漏。

最終我用ASIHTTPRequest替換了NSURLConnection,崩潰完全停止。 CFNetwork 差不多永遠不會泄漏,但是,解析DNS名稱時仍會出現一個非常罕見的泄漏。我現在很滿意。希望這些信息能爲你節省一些時間!

+0

我建議不要讓線程在手機上佔用100%的CPU。你會很快耗盡電池。 – 2009-12-21 17:42:56

+0

如果用戶想要更多內容,他們將滾動以獲取內容。 CPU僅在用戶請求時才使用。可可的線程模型不應該有這樣的問題。跟蹤UI事件和後臺線程僅爲0.5時,主線程以1.0優先級運行。我想爲主線程分配有保證的CPU時間。那可能嗎? – 2009-12-21 19:10:59

回答

7

實際上,您不能擁有兩個或三個以上的後臺網絡線程,並且UI保持完全響應。

優化用戶響應能力,這是用戶真正注意到的唯一事情。或者(我真的很討厭這麼說)在你的應用程序中添加一個「Turbo」按鈕,這個按鈕會啓動一個非交互式模式對話框,並在併發操作數增加時將併發操作數增加到10。

+1

「在你的應用程序中添加一個」Turbo「按鈕」我從未想到這一點,但這是一個甜蜜的主意!我希望儘可能快地設計應用程序,如果可能,最多可以連接10個連接,並根據需要進行退出以保持UI性能。 「Turbo」按鈕對於舊設備上的用戶來說是一個不錯的選擇,他們不介意犧牲每秒幾幀來更快地獲取內容。感謝您的想法! – 2009-12-21 17:47:53

3

聽起來好像NSOperationQueueDefaultMaxConcurrentOperationCount設置爲1的原因!我想你只是讓你的手機超負荷。您可能能夠解決線程優先級問題 - 我認爲Mach核心是可用的,並且是正式祝福的API的一部分 - 但對我來說,這聽起來像是錯誤的方法。

使用「系統」常量的優點之一是,蘋果可以爲你調整應用程序。你如何調整這個在原始iPhone上運行?對於明年的四核iPhone來說足夠高嗎?

+0

我實際上是爲用戶而不是設備優化設計。我們的大腦每兩年的容量不會加倍,或者他們?)。即使機器能夠同時獲取1000個對象,我也不想。如果我向用戶投入錯誤的內容,他們會變得不知所措,或者更糟糕,無聊。 – 2009-12-21 17:18:43

+1

如果你消耗的CPU太多,主(UI)線程就會餓死 - 那麼你並沒有爲用戶進行優化,而是在優化網絡吞吐量。他們是兩個完全不同的東西。優化用戶意味着平衡可用資源,但通常意味着「保持UI的響應」。首先這是主要的規則,只要UI仍然響應,用戶可以再坐幾秒鐘。 – 2009-12-21 17:29:23

+0

忘記回答你關於原iPhone的問題。我希望它儘可能快地消耗所有可用的CPU時間。如果一次不能跟上10個連接,那就這樣吧。我可以隨時優化以擠出一些額外的表現。我需要確保所有這些都是在不增加主線程延遲的情況下完成的。 – 2009-12-21 17:30:13

0

James,儘管我沒有遇到過你的問題,但我成功的是在NSOperation子類中使用同步連接進行下載。

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

我用這種方法從網絡位置抓取圖像資產和更新目標UIImageView秒。下載發生在NSOperationQueue,更新圖像視圖的方法在主線程上執行。

+0

我實際上也嘗試過使用同步請求,但它似乎執行相同的操作。這很有道理,因爲Apple的文檔說同步連接是建立在異步連接之上的。 – 2009-12-21 17:53:15