ios - Grand Central Dispatch vs. NSThread
cocoa-touch grand-central-dispatch (3)
Debido a que su dispositivo solo tiene un procesador, GCD probablemente solo crea un hilo para ejecutar bloques y sus bloques se ejecutan secuencialmente. Sin embargo, ha creado 10 subprocesos diferentes y cada uno obtiene una pequeña parte del tiempo de procesamiento disponible. Afortunadamente, dormir no requiere un uso intensivo del procesador, por lo que todos sus hilos se combinan bastante bien. Pruebe una prueba similar en una máquina con 4 u 8 núcleos de procesamiento, y verá que GCD ejecuta más bloques en paralelo.
Lo bueno de GCD no es que necesariamente ofrezca un mejor rendimiento que los subprocesos, es que el programador no tiene que pensar en crear subprocesos o hacer coincidir el número de subprocesos con el número de procesadores disponibles. Puede crear muchas pequeñas tareas que se ejecutarán a medida que el procesador esté disponible y dejar que el sistema programe esas tareas para usted.
Edición: jugué un poco con su código en un sencillo programa de línea de comandos en mi Mac. Como sugerí en mi comentario a continuación, y también mencionado en la respuesta de @ Ren-D, usar dispatch_async()
lugar de dispatch_apply()
hace una gran diferencia. Aquí está el código que utilicé:
- (void)doIt:(NSNumber *)i
{
for (int j = 0; j < MAX_COUNT; j++)
;
NSLog(@"Thread#%i", [i intValue]);
}
- (void)doWork:(id)sender
{
for (int i = 0; i<10; i++) {
NSNumber *t = [NSNumber numberWithInt:i];
[NSThread detachNewThreadSelector:@selector(doIt:) toTarget:self withObject:t];
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (size_t i = 0; i<10; i++) {
dispatch_async(queue, ^(void) {
for (int j = 0; j < MAX_COUNT; j++)
;
NSLog(@"GCD#%u",(int)i);
});
}
NSLog(@"Done.");
sleep(15);
}
Como puede ver, reemplacé sus llamadas sleep()
con bucles que pasan algún tiempo contando. (Ejecuté el código en una MacBook Pro; es posible que desee ajustar el valor de MAX_COUNT
hacia abajo si está ejecutando en un iPhone.) Si usa sleep()
tanto en los subprocesos como en el bloque, dispatch_async()
hace los bloques se comportan como los hilos: todos los bloques se ejecutan simultáneamente y se completan aproximadamente al mismo tiempo. Cambiando a contar los cambios de comportamiento, todos los subprocesos se ejecutan simultáneamente, pero los bloques se ejecutan en grupos (mi máquina tiene dos núcleos de procesador, por lo que ejecutó los bloques en grupos de dos). Esto es exactamente lo que cabría esperar; El trabajo de GCD es poner en cola las tareas y terminarlas lo más rápido posible haciendo el mejor uso de los recursos disponibles, no ejecutando tantas tareas simultáneamente como sea posible.
Aquí está la salida del código anterior:
2011-04-14 02:48:46.840 BlockTest[14969:903] Hello, World!
2011-04-14 02:48:47.104 BlockTest[14969:903] Done.
2011-04-14 02:48:52.834 BlockTest[14969:1503] Thread#0
2011-04-14 02:48:52.941 BlockTest[14969:4f03] GCD#0
2011-04-14 02:48:52.952 BlockTest[14969:5003] GCD#1
2011-04-14 02:48:52.956 BlockTest[14969:4703] Thread#8
2011-04-14 02:48:53.030 BlockTest[14969:3703] Thread#4
2011-04-14 02:48:53.074 BlockTest[14969:2b03] Thread#1
2011-04-14 02:48:53.056 BlockTest[14969:4b03] Thread#9
2011-04-14 02:48:53.065 BlockTest[14969:3b03] Thread#5
2011-04-14 02:48:53.114 BlockTest[14969:3303] Thread#3
2011-04-14 02:48:53.138 BlockTest[14969:4303] Thread#7
2011-04-14 02:48:53.147 BlockTest[14969:3f03] Thread#6
2011-04-14 02:48:53.156 BlockTest[14969:2f03] Thread#2
2011-04-14 02:48:53.909 BlockTest[14969:4f03] GCD#2
2011-04-14 02:48:53.915 BlockTest[14969:5003] GCD#3
2011-04-14 02:48:54.700 BlockTest[14969:4f03] GCD#4
2011-04-14 02:48:54.721 BlockTest[14969:5003] GCD#5
2011-04-14 02:48:55.508 BlockTest[14969:4f03] GCD#6
2011-04-14 02:48:55.550 BlockTest[14969:5003] GCD#7
2011-04-14 02:48:56.321 BlockTest[14969:4f03] GCD#8
2011-04-14 02:48:56.345 BlockTest[14969:5003] GCD#9
Tenga en cuenta que dos de los bloques realmente terminaron antes de todos menos uno de los hilos. También: la sleep(15)
al final del código está justo ahí para permitir que las hebras y los bloques registren sus mensajes antes de que finalice el programa. Dependiendo de en qué tipo de programa pegue el código, es posible que no lo necesite.
Creé algunos códigos de prueba para NSThread y Grand Central Dispatch (GCD):
- (void)doIt:(NSNumber *)i
{
sleep(1);
NSLog(@"Thread#%i", [i intValue]);
}
- (IBAction)doWork:(id)sender
{
for (int i = 0; 10 > i; i++) {
NSNumber *t = [NSNumber numberWithInt:i];
[NSThread detachNewThreadSelector:@selector(doIt:) toTarget:self withObject:t];
}
sleep(1);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t i) {
sleep(1);
NSLog(@"GCD#%u",(int)i);
});
}
Y los resultados:
2011-04-13 19:41:07.806 GDC[1494:5e03] Thread#0
2011-04-13 19:41:07.813 GDC[1494:6903] Thread#3
2011-04-13 19:41:07.812 GDC[1494:6403] Thread#2
2011-04-13 19:41:07.812 GDC[1494:5f03] Thread#1
2011-04-13 19:41:07.813 GDC[1494:6e03] Thread#4
2011-04-13 19:41:07.814 GDC[1494:7303] Thread#5
2011-04-13 19:41:07.814 GDC[1494:7803] Thread#6
2011-04-13 19:41:07.815 GDC[1494:7d03] Thread#7
2011-04-13 19:41:07.815 GDC[1494:8203] Thread#8
2011-04-13 19:41:07.816 GDC[1494:8703] Thread#9
2011-04-13 19:41:08.812 GDC[1494:707] GCD#0
2011-04-13 19:41:09.816 GDC[1494:707] GCD#1
2011-04-13 19:41:10.819 GDC[1494:707] GCD#2
2011-04-13 19:41:11.825 GDC[1494:707] GCD#3
2011-04-13 19:41:12.828 GDC[1494:707] GCD#4
2011-04-13 19:41:13.833 GDC[1494:707] GCD#5
2011-04-13 19:41:14.838 GDC[1494:707] GCD#6
2011-04-13 19:41:15.848 GDC[1494:707] GCD#7
2011-04-13 19:41:16.853 GDC[1494:707] GCD#8
2011-04-13 19:41:17.857 GDC[1494:707] GCD#9
Los NSThreads funcionan como esperaba: las tareas se ejecutan simultáneamente y cada subproceso se duerme durante 1 segundo.
El dispatch_apply no funciona como esperaba: ¿por qué es secuencial el orden? ¿Por qué cada bucle espera hasta que finalice el bucle anterior?
Gracias por la ayuda.
Intente ver este sitio web: http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
En el entorno IOS, se dice que dispatch_apply
dependerá de la cola pasada, si la cola de destino es una cola concurrente devuelta por dispatch_get_global_queue
(que es su caso), el bloque puede invocarse simultáneamente.
Entonces, creo que está funcionando, solo sucede que se ejecuta como si se ejecutara de forma asíncrona. Además, en qué dispositivo se ejecuta el código podría jugar un papel en el resultado (como lo menciona @Caleb). Pero mi sugerencia es, tal vez intente dispatch_async
lugar?
Si alguien quiere probar qué método es el mejor para el problema de spedify, aquí está el código:
#define MAX_COUNT 99999999
#define HOW_MUCH 10
- (void)doIt:(NSNumber *)i
{
for (int j = 0; j < MAX_COUNT; j++)
;
NSLog(@"Thread#%i", [i intValue]);
}
- (IBAction)doWork:(id)sender
{
NSLog(@"START");
for (int i = 0; i < HOW_MUCH; i++) {
NSNumber *t = [NSNumber numberWithInt:i];
[NSThread detachNewThreadSelector:@selector(doIt:) toTarget:self withObject:t];
}
sleep(3);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(HOW_MUCH, queue, ^(size_t i) {
for (int j = 0; j < MAX_COUNT; j++)
;
NSLog(@"GCD APPLY %u",(int)i);
});
sleep(3);
for (size_t k = 0; k < HOW_MUCH; k++) {
dispatch_async(queue, ^(void) {
for (int j = 0; j < MAX_COUNT; j++)
;
NSLog(@"GCD ASYNC#%u",(int)k);
});
}
sleep(10);
NSLog(@"DONE");
}