Los subprocesos de fondo que consumen el 100% de la CPU en el iPhone 3GS causan un hilo principal latente

En mi aplicación estoy ejecutando 10 NSURLConnections asíncronos dentro de una NSOperationQueue como NSInvocationOperations. Para evitar que cada operación regrese antes de que la conexión haya tenido la oportunidad de finalizar, llamo a CFRunLoopRun () como se ve aquí:

- (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];
}

Una vez que finaliza la conexión, el selector de delegado de la conexión final llama a CFRunLoopStop (CFRunLoopGetCurrent ()) para reanudar la ejecución en connectInBackground (), lo que le permite regresar normalmente:

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

Esto funciona bien y es seguro para subprocesos porque agrupé la respuesta y los datos de cada conexión como variables de instancia en la subclase TTURLConnection.

NSOperationQueue afirma que dejar su número máximo de operaciones concurrentes como NSOperationQueueDefaultMaxConcurrentOperationCount le permite ajustar el número de operaciones dinámicamente, sin embargo, en este caso, siempre decide que 1 es suficiente. Dado que eso no es lo que quiero, he cambiado el número máximo a 10 y ahora lo jala seriamente.

El problema con esto es que estos hilos (con la ayuda de SpringBoard y DTMobileIS) consumen todo el tiempo de CPU disponible y hacen que el hilo principal quede latente. En otras palabras, una vez que la CPU se utiliza al 100%, el subproceso principal no procesa los eventos de la IU tan rápido como lo necesita para mantener una IU sin problemas. Específicamente, el desplazamiento de la vista de tabla se vuelve inestable.

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

Mientras el usuario interactúa con la pantalla o la tabla se desplaza, la prioridad del subproceso principal se convierte en 1.0 (la más alta posible) y su modo de bucle de ejecución se convierte en UIEventTrackingMode. Cada uno de los hilos de la operación tiene una prioridad de 0.5 por defecto y las conexiones asíncronas se ejecutan en el NSDefaultRunLoopMode. Debido a mi comprensión limitada de cómo los subprocesos y sus bucles de ejecución interactúan en función de las prioridades y los modos, estoy perplejo.

¿Hay alguna forma de consumir de forma segura todo el tiempo de CPU disponible en los subprocesos de fondo de mi aplicación y al mismo tiempo garantizar que su subproceso principal reciba la mayor cantidad de CPU que necesita? ¿Tal vez forzando el hilo principal para que se ejecute tan a menudo como sea necesario? (Pensé que las prioridades del hilo habrían tenido cuidado de eso).

ACTUALIZACIÓN 12/23: Finalmente, comencé a entender el CPU Sampler y encontré la mayoría de las razones por las que la IU se estaba poniendo nerviosa. En primer lugar, mi software estaba llamando a una biblioteca que tenía semáforos de exclusión mutua. Estos bloqueos bloquearon el hilo principal durante cortos períodos de tiempo, lo que provocó que el desplazamiento saltara ligeramente.

Además, encontré algunas llamadas costosas de NSFileManager y funciones de hashing md5 que tardaban demasiado tiempo en ejecutarse. La asignación de objetos grandes con demasiada frecuencia causó algunos otros éxitos de rendimiento en el hilo principal.

He empezado a resolver estos problemas y el rendimiento ya es mucho mejor que antes. Tengo 5 conexiones simultáneas y el desplazamiento es suave, pero todavía tengo más trabajo que hacer. Estoy planeando escribir una guía sobre cómo usar el CPU Sampler para detectar y solucionar problemas que afectan el rendimiento del subproceso principal. Gracias por los comentarios hasta ahora, fueron útiles!

ACTUALIZACIÓN 14/01/2010: Después de lograr un rendimiento aceptable, comencé a darme cuenta de que el marco de CFNetwork estaba perdiendo memoria ocasionalmente. ¡Las excepciones fueron aleatorias (sin embargo, raramente) también se plantearon dentro de CFNetwork! Intenté todo lo que pude para evitar esos problemas pero nada funcionó. Estoy bastante seguro de que los problemas se deben a defectos dentro de NSURLConnection. Escribí programas de prueba que no hacían nada, excepto ejercitar la conexión NSUR, y aún seguían estrellándose y goteando.

En última instancia, he sustituido NSURLConnection conASIHTTPRequest y el choque se detuvo por completo. Red de redcasi sin embargo, nunca se producen fugas, todavía hay una fuga muy rara que se produce al resolver un nombre DNS. Estoy bastante satisfecho ahora. Esperemos que esta información te ahorre tiempo!

Respuestas a la pregunta(3)

Su respuesta a la pregunta