En mi aplicación estoy ejecutando 10 NSURLConnections asincrónicas dentro de un NSOperationQueue como NSInvocationOperations. Con el fin de evitar que cada operación de volver antes de que la conexión ha tenido la oportunidad de terminar llamo CFRunLoopRun() como se ve aquí:Subprocesos de fondo que consumen 100% de CPU en el iPhone 3GS provoca el hilo principal latente
- (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 finalizada la conexión, el selector último delegado respecto, pide CFRunLoopStop (CFRunLoopGetCurrent()) para reanudar la ejecución en connectInBackground(), lo que le permite volver 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 hilo de seguridad porque liado respuesta y los datos como variables de instancia en la subclase TTURLConnection de cada conexión.
NSOperationQueue afirma que dejando su número máximo de operaciones simultáneas como NSOperationQueueDefaultMaxConcurrentOperationCount le permite ajustar el número de operaciones dinámicamente, sin embargo, en este caso siempre decide que 1 es suficiente. Como eso no es lo que quiero, he cambiado el número máximo a 10 y ahora se lo dejo en serio.
El problema con esto es que estos subprocesos (con la ayuda de SpringBoard y DTMobileIS) consumen todo el tiempo de CPU disponible y hacen que el hilo principal se vuelva latente. En otras palabras, una vez que la CPU se utiliza al 100%, el hilo principal no procesa los eventos de IU tan rápido como lo necesita para mantener una interfaz de usuario uniforme. Específicamente, el desplazamiento de la vista de tabla se pone nervioso.
Process Name % CPU
SpringBoard 45.1
MyApp 33.8
DTMobileIS 12.2
...
Mientras que el usuario interactúa con la pantalla o la mesa se desplaza la prioridad del hilo 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 subprocesos 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 limitada comprensión de cómo los hilos 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 hilos de fondo de mi aplicación, al mismo tiempo que garantizo que su hilo principal recibe la mayor cantidad de CPU que necesita? ¿Quizás forzando el hilo principal a ejecutarse tantas veces como sea necesario? (Pensé prioridades de los hilos se han ocupado de eso.)
ACTUALIZACIÓN 12/23: fin he empezado a conseguir una manija en el sampler de la CPU y se encontró la mayor parte de las razones por las que la interfaz de usuario se estaba convirtiendo nervioso. En primer lugar, mi software llamaba a una biblioteca que tenía semáforos de exclusión mutua. Estos bloqueos bloqueaban el hilo principal durante cortos periodos de tiempo, lo que provocaba que el desplazamiento saltara un poco.
Además, encontré algunas costosas llamadas a NSFileManager y funciones de hash md5 que tomaban demasiado tiempo para ejecutarse. La asignación de objetos grandes con demasiada frecuencia causó algunos otros golpes de rendimiento en el hilo principal.
Comencé 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 por hacer. Estoy planeando escribir una guía sobre cómo usar la Muestra de CPU para detectar y solucionar problemas que afectan el rendimiento del hilo principal. Gracias por los comentarios hasta ahora, ¡fueron útiles!
ACTUALIZACIÓN 1/14/2010: Después de lograr un rendimiento aceptable comencé a darme cuenta de que el marco CFNetwork estaba perdiendo memoria ocasionalmente.Las excepciones fueron al azar (sin embargo, rara vez) se criaron dentro de CFNetwork también! Intenté todo lo posible 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 hicieron nada excepto ejercitar NSURLConnection y todavía estaban cayendo y goteando.
En última instancia reemplacé NSURLConnection con ASIHTTPRequest y el bloqueo se detuvo por completo. CFNetwork casi nunca gotea, sin embargo, todavía hay una fuga muy rara que se produce al resolver un nombre DNS. Estoy bastante satisfecho ahora. ¡Espero que esta información te ahorre algo de tiempo!
Yo recomendaría no tener hilos que consuman 100% de CPU de todos modos en un teléfono. Vaciará la batería rápidamente. –
Si el usuario quiere más contenido, se desplaza para obtenerlo. La CPU solo se usa cuando el usuario lo solicita. El modelo de subprocesamiento de Cocoa no debería tener un problema al hacer esto. El hilo principal se ejecuta con una prioridad de 1.0 al rastrear eventos de UI y mis hilos de fondo son solo 0.5. Quiero asignar tiempo de CPU garantizado para el hilo principal. ¿Es eso posible? –