Фоновые потоки, использующие 100% ЦП на iPhone 3GS, вызывают скрытый основной поток

В моем приложении я выполняю 10 асинхронных NSURLConnections в NSOperationQueue как 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];
}

После того как соединение заканчивается, делегат окончательного соединения, которого селектор называет 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) используют все доступное процессорное время и заставляют основной поток становиться скрытым. Другими словами, после того как ЦП составляет используемых 100%, основной поток не обрабатывает события UI с такой скоростью, как ему нужно к тому, для поддержания гладкого UI. А именно, табличное представление, прокручивающее, становится нервным.

Process Name  % CPU
SpringBoard   45.1
MyApp         33.8
DTMobileIS    12.2
...

В то время как пользователь взаимодействует с экраном, или таблица прокручивает приоритет основного потока, становится 1.0 (максимально возможное), и его режим цикла выполнения становится UIEventTrackingMode. Каждый из потоков операции является 0,5 приоритетами по умолчанию и асинхронными соединениями, выполненными в NSDefaultRunLoopMode. Из-за моего ограниченного понимания того, как потоки и их циклы выполнения взаимодействуют на основе приоритетов и режимов, я озадачен.

Существует ли способ безопасно использовать все доступное процессорное время в фоновых потоках моего приложения, все еще гарантируя, что его основной поток дан такое количество ЦП, как этому нужно? Возможно, вынуждая основной поток работать так часто, как это должно? (Я думал, что приоритеты потока будут заботиться об этом.)

ОБНОВЛЕНИЕ 12/23: Я наконец начал разобраться с Сэмплером ЦП и нашел большинство причин, почему UI становился нервным. В первую очередь, мое программное обеспечение называло библиотеку, которая имела семафоры взаимного исключения. Эти блокировки блокировали основной поток в течение коротких промежутков времени, заставляя прокрутку пропустить немного.

Кроме того, я нашел некоторые дорогие вызовы NSFileManager и md5 хеш-функции, которые занимали слишком много времени для выполнения. Выделение больших объектов слишком часто вызывало некоторые другие хиты производительности в основном потоке.

Я начал решать эти вопросы, и производительность уже намного лучше, чем прежде. У меня есть 5 одновременных соединений, и прокрутка является гладкой, но у меня все еще есть больше работы, чтобы сделать. Я планирую записать руководству по тому, как использовать Сэмплер ЦП, чтобы обнаружить и устранить проблемы, которые влияют на производительность основного потока. Спасибо за комментарии до сих пор, они были полезны!

ОБНОВЛЕНИЕ 14.01.2010: После достижения приемлемой производительности я начал понимать, что платформа CFNetwork пропускала память иногда. Исключения были случайным образом (однако, редко) повышаемый в CFNetwork также! Я попробовал все, что я мог для предотвращения тех проблем, но ничто не работало. Я совершенно уверен, что проблемы происходят из-за дефектов в самом NSURLConnection. Я записал тестовые программы, которые не сделали ничего кроме осуществления NSURLConnection, и они все еще отказывали и протекали.

В конечном счете я заменил NSURLConnection ASIHTTPRequest и катастрофическим отказом, остановленным полностью. CFNetwork почти никогда не протекает, однако, существует все еще одна очень редкая утечка, которая происходит при разрешении имени DNS. Я вполне удовлетворен теперь. Надо надеяться, эта информация экономит Вам некоторое время!

10
задан James Wald 15 January 2010 в 03:27
поделиться

3 ответа

На практике у вас просто не может быть больше двух или трех фоновых сетевых потоков, а пользовательский интерфейс остается полностью отзывчивым.

Оптимизируйте реакцию пользователя, это единственное, что пользователь действительно замечает. Или (и мне очень неприятно это говорить) добавьте в свое приложение кнопку «Турбо», которая открывает неинтерактивный модальный диалог и увеличивает количество одновременных операций до 10, пока оно работает.

7
ответ дан 4 December 2019 в 01:31
поделиться

Похоже, что NSOperationQueueDefaultMaxConcurrentOperationCount не зря установлено в 1! Я думаю, ты просто перегружаешь свой бедный телефон. Вы можете повозиться с приоритетами потоковой передачи - я думаю, что ядро ​​Mach доступно и является частью официально одобренного API - но для меня это звучит как неправильный подход.

Одно из преимуществ использования «системы» constants - это то, что Apple может настроить приложение под вас. Как вы собираетесь настроить его для работы на оригинальном iPhone? Достаточно ли 10 для четырехъядерного iPhone в следующих годах?

3
ответ дан 4 December 2019 в 01:31
поделиться

Джеймс, хотя я не сталкивался с вашей проблемой, мне удалось использовать синхронное соединение для загрузки в подклассе NSOperation .

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

I используйте этот подход для получения ресурсов изображений из сетевых местоположений и обновления целевых UIImageView s. Загрузка происходит в NSOperationQueue , а метод обновления представления изображения выполняется в основном потоке.

0
ответ дан 4 December 2019 в 01:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: