【iOS】Resumen de problemas de bloqueo y subprocesos múltiples

Prefacio

Resumen de bloqueos de iOS y subprocesos múltiples

1. Subprocesos múltiples que entiendes

Multithreading es la capacidad de ejecutar múltiples threads (subtareas) simultáneamente para mejorar el rendimiento y la capacidad de respuesta del programa. Permite procesar múltiples tareas simultáneamente en un programa.

  • Concurrencia: se ejecutan varios subprocesos simultáneamente en un período de tiempo y la computadora implementa tareas de múltiples subprocesos cambiando entre diferentes subprocesos.

ventaja

  1. Mejora enormemente la velocidad de ejecución del programa.
  2. El uso de subprocesos puede hacer que las tareas a largo plazo se procesen más adelante, mejorando así la experiencia del usuario.

defecto

  1. Si hay una gran cantidad de subprocesos, el rendimiento se verá afectado porque el sistema operativo necesita cambiar entre ellos.
  2. Más subprocesos requieren más espacio de memoria.

2. La diferencia entre atómico y no atómico y sus funciones.

  1. Operación atómica atómica: bloqueo para garantizar la seguridad de los subprocesos de los métodos de acceso setter y getter (solo los métodos setter y getter están bloqueados) . Debido a que el hilo está bloqueado, cuando otros hilos acceden al atributo actual, primero completarán la operación actual del atributo.
  • Las operaciones establecer y obtener en el mismo objeto se realizan de forma secuencial.
  • La velocidad no es rápida porque la operación debe completarse en su totalidad.
  • La seguridad de subprocesos requiere consumir muchos recursos del sistema para bloquear atributos.
  • El uso atomicno garantiza la seguridad absoluta de los subprocesos, porque solo bloquea los métodos y atomicgenerados por el sistema . Para operaciones que garantizan absolutamente la seguridad de los subprocesos, se debe utilizar un método más avanzado. @lock garantiza la seguridad de los subprocesos.settergetterNSSpinLocksyncronized
  1. Nanatomic es una operación no atómica, no se bloquea y la ejecución de subprocesos es rápida, pero el acceso de varios subprocesos a la misma propiedad puede provocar un bloqueo.
  • No es el predeterminado
  • Es más rápido. Si dos subprocesos acceden a la misma propiedad, puede provocar un bloqueo.
  • No es seguro para subprocesos
  1. La principal diferencia entre atómico y no atómico es que los métodos getter/setter generados automáticamente por el sistema son diferentes.
  • atomicgetterEl método / generado automáticamente por el sistema setterrealizará operaciones de bloqueo.
  • nonatomicgetterEl método / generado automáticamente por el sistema setterno se bloqueará.

⚠️: atomicLos atributos modificados, generados por el sistema, getter/settergarantizarán getla setintegridad de la operación y no se verán afectados por otros subprocesos. Por ejemplo, el método getter del hilo A se está ejecutando a la mitad y el hilo B lo llama setter: entonces getterel método getter del hilo A aún puede obtener un objeto intacto.

3. Tipos de cola de GCD: tres tipos de cola

  1. La cola principal (cola en serie del hilo principal) tiene la misma función que el hilo principal: las tareas enviadas a la cola principal se ejecutarán en el hilo principal.
dispatch_get_main_queue() 来获取
  1. Cola global (cola concurrente global) La cola concurrente global es compartida por todo el proceso y tiene cuatro niveles de prioridad: escuela secundaria (el valor predeterminado es medio), baja y fondo.
dispatch_get_global_queue() 可以设置优先级
  1. La cola personalizada puede ser en serie o concurrente.
dispatch_queue_create()

4. Problema de punto muerto de GCD

Cuatro condiciones necesarias para el punto muerto del hilo

  • Exclusión mutua : un recurso sólo puede ser ocupado por un proceso a la vez.
  • Poseído y en espera : Un proceso en sí ocupa uno o más recursos, y al mismo tiempo hay recursos insatisfechos y está esperando que otros procesos liberen los recursos.
  • No se puede apropiar : otros ya han ocupado un determinado recurso y no puede apropiarse de otros recursos porque necesita el recurso.
  • Espera circular : existe una cadena de procesos tal que cada proceso ocupa al menos un recurso requerido por el siguiente proceso.

Concepto: El llamado punto muerto suele ocurrir cuando dos subprocesos A y B están atascados. A está esperando a B y B está esperando a A, esperándose entre sí hasta que el valor llega al punto muerto.

  1. La cola en serie del hilo principal ejecuta tareas sincrónicamente. Cuando el hilo principal se está ejecutando, se producirá un punto muerto.
NSLog(@"1"); // 任务1
dispatch_sync(dispatch_get_main_queue(), ^{
    
    
    NSLog(@"2"); // 任务2
});
NSLog(@"3"); // 任务3

analizar:

  • dispatch_syncRepresenta un hilo sincronizado;
  • dispatch_get_main_queueRepresenta la cola principal que se ejecuta en el hilo principal;
  • La tarea 2 es la tarea de sincronizar subprocesos.
  • La tarea 3 debe esperar a que finalice la tarea 2 antes de ejecutarla.

¿Por qué causa un punto muerto?

  1. Primero ejecute la tarea 1, lo cual definitivamente no es un problema, pero luego, si el programa encuentra un hilo de sincronización, entrará en espera, esperará a que se complete la tarea 2 y luego ejecutará la tarea 3. Pero esta es la cola principal, una cola en serie especial. Cuando llega una tarea, por supuesto se agregará al final de la cola y luego la FIFOtarea se ejecutará de acuerdo con el principio. Luego, ahora la tarea 2 se agregará al final y la tarea 3 se clasificará delante de la tarea 2.

La Tarea 3 no se puede ejecutar hasta que se complete la Tarea 2, y la Tarea 2 está clasificada detrás de la Tarea 3, lo que significa que la Tarea 2 no se puede ejecutar hasta que se complete la Tarea 3, por lo que entraron en una situación de espera el uno por el otro. [En este caso, quedémonos aquí] Esto es un punto muerto.

Por favor agregue la descripción de la imagen.

  1. Sincrónico y asincrónico anidados entre sí
// 同步 + 异步 互相嵌套产生死锁
- (void)sync_async {
    
    
    dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1"); // 任务1
    dispatch_async(queue, ^{
    
    
        NSLog(@"2"); // 任务2
        dispatch_sync(queue, ^{
    
    
            NSLog(@"3"); // 任务3
        });
        NSLog(@"4"); // 任务4
    });
    NSLog(@"5"); // 任务5
}

Por favor agregue la descripción de la imagen.
Análisis: Primero, la función despacho_queue_create crea una cola en serie DISPATCH_QUEUE_SERIAL a través de una cola personalizada.

  1. Realizar la tarea 1.
  2. Cuando encuentre un subproceso asincrónico, agregue [Tarea 2, Subproceso síncrono, Tarea 4] a la cola en serie. Debido a que es un subproceso asincrónico, la tarea 5 en el subproceso principal no tiene que esperar a que se completen todas las tareas en el subproceso asincrónico;
  3. Debido a que la tarea 5 no tiene que esperar, no se puede determinar el orden de salida de 2 y 5;
  4. Después de ejecutar la tarea 2, se encuentra un hilo de sincronización y en este momento la tarea 3 se agrega a la cola en serie;
  5. Y debido a que la tarea 4 se une a la cola en serie antes que la tarea 3, la tarea 3 debe esperar a que se complete la tarea 4 antes de poder ejecutarse. Sin embargo, el hilo de sincronización donde se encuentra la tarea 3 se bloqueará, por lo que la tarea 4 debe esperar a que la tarea 3 termine de ejecutarse antes de ejecutarla. Esto volverá a caer en una espera infinita, provocando un punto muerto.
    Insertar descripción de la imagen aquí
    Bucle infinito del hilo principal
- (void)async_loop {
    
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
    
        NSLog(@"1"); // 任务1
        dispatch_sync(dispatch_get_main_queue(), ^{
    
    
            NSLog(@"2"); // 任务2
        });
        NSLog(@"3"); // 任务3
    });
    NSLog(@"4"); // 任务4
    while (1) {
    
    
    }
    NSLog(@"5"); // 任务5
    
    // a打印 4 1 / 1 4 顺序不定   
}

Resultado de impresión: 4 1 / 1 4 en orden incierto

analizar:

  • Primero, echemos un vistazo a qué tareas se han agregado a la cola principal: [hilo asíncrono, tarea 4, bucle infinito, tarea 5].
  • Las tareas agregadas al subproceso asincrónico de la cola global son: [Tarea 1, Subproceso síncrono, Tarea 3].
  • El primero es un hilo asincrónico. La tarea 4 no necesita esperar, por lo que el orden de la tarea 1 y la tarea 4 no es necesariamente seguro.
  • Una vez completada la tarea 4, el programa entra en un bucle infinito y la cola principal se bloquea. Sin embargo, el subproceso asincrónico agregado a la cola global no se ve afectado y continúa ejecutando el subproceso de sincronización detrás de la tarea 1.
  • En el hilo de sincronización, la tarea 2 se agrega al hilo principal y la tarea 3 espera a que se complete la tarea 2 antes de poder ejecutarse. En este momento, el hilo principal ha sido bloqueado por un bucle infinito. Por lo tanto, la tarea 2 no se puede ejecutar y, por supuesto, la tarea 3 no se puede ejecutar y la tarea 5 después del bucle infinito no se ejecutará.

Al final, sólo podemos obtener los resultados de 1 y 4 en un orden incierto.

5. Diferencias y conexiones entre subprocesos múltiples

Insertar descripción de la imagen aquí

GCD y NSOperación

  • GCDLa eficiencia de ejecución es mayor, ejecuta tareas compuestas por bloques, es una estructura de datos liviana y es más conveniente de escribir.
  • GCDSolo FIFOse admiten colas y NSOperationQueueel orden de ejecución se puede ajustar estableciendo el número máximo de concurrencias, estableciendo prioridades y agregando dependencias.
  • NSOperationLas dependencias se pueden establecer entre colas GCDy el orden de ejecución solo se puede controlar mediante métodos como vallas.
  • NSOperationEstá más orientado a objetos y admite KVOsubclases que también se pueden agregar mediante herencia y otras relaciones.
  • Entonces, si necesitamos considerar la secuencia y las dependencias entre operaciones asincrónicas, como descargas simultáneas de subprocesos múltiples, etc., useNSOperation

La diferencia entre GCD y NSThread

  • NSThreadAl @selectorespecificar el método a ejecutar, el código se dispersa y se basa en la NSObjectclasificación para implementar la comunicación entre subprocesos. Si desea abrir un subproceso, debe crear varios objetos de subproceso. Lo que se usa a menudo es [NSTread current]ver el hilo actual.
  • NSThreadEs un objeto que controla la ejecución del hilo, no es tan NSOperationabstracto, a través de él podemos obtener fácilmente un hilo y controlarlo. Pero NSThreadel control de concurrencia entre subprocesos debe ser controlado por nosotros mismos, lo que se puede NSConditionlograr mediante.
  • GCDAl blockespecificar el código que se ejecutará, el código se concentra y todo el código se escribe en conjunto, lo que hace que el código sea más simple, más fácil de leer y mantener, ¡y no hay necesidad de administrar el proceso de creación/destrucción/reutilización de subprocesos! Los programadores no necesitan preocuparse por el ciclo de vida de los subprocesos.

6. ¿Procesos e hilos?

Referencia: Conceptos, diferencias entre procesos e hilos, y comunicación entre procesos e hilos
1. Conceptos básicos:

  • Un proceso es una encapsulación de un programa en tiempo de ejecución y es la unidad básica para la programación y asignación de recursos en el sistema, logrando la concurrencia del sistema operativo.
  • Un subproceso es una subtarea de un proceso y la unidad básica de programación y asignación de CPU. Se utiliza para garantizar el rendimiento en tiempo real de la ejecución del programa y lograr la concurrencia dentro del proceso. Un hilo es la unidad de ejecución y programación más pequeña reconocida por el sistema operativo . Cada hilo ocupa solo un procesador virtual , etc. Cada hilo completa diferentes tareas, pero comparte el mismo espacio de direcciones (es decir, la misma memoria dinámica, archivos mapeados, código objeto, etc.), colas de archivos abiertos y otros recursos del núcleo.

2. Diferencia:

  • Un hilo solo puede pertenecer a un proceso y un proceso puede tener varios hilos , pero debe haber al menos un hilo. Los hilos dependen de procesos para su existencia.
  • El proceso es la unidad más pequeña de asignación de recursos y el subproceso es la unidad más pequeña de programación de CPU.
  • Un proceso tiene una unidad de memoria independiente durante la ejecución y varios subprocesos comparten la memoria del proceso . (Los recursos se asignan a los procesos y todos los subprocesos del mismo proceso comparten todos los recursos del proceso. Varios subprocesos en el mismo proceso comparten segmento de código (código y constantes), segmento de datos (variables globales y variables estáticas), segmento extendido (montón almacenamiento). Pero cada subproceso tiene su propio segmento de pila, que también se denomina período de ejecución y se utiliza para almacenar todas las variables locales y variables temporales).
  • Los procesos no se afectarán entre sí; subprocesos: si un subproceso se cuelga, todo el proceso se colgará.
  • La comunicación entre subprocesos es más conveniente: los subprocesos en el mismo proceso comparten datos como variables globales y variables estáticas .

3. Método de comunicación :

Método de comunicación entre procesos.
  1. La comunicación entre procesos incluye principalmente canalizaciones, IPC del sistema (incluidas colas de mensajes, semáforos, señales, memoria compartida, etc.) y sockets.
  2. El semáforo es diferente de la estructura IPC que se ha introducido: es un contador que se puede utilizar para controlar el acceso a recursos compartidos por parte de múltiples procesos . Los semáforos se utilizan para implementar la exclusión mutua y la sincronización entre procesos, en lugar de almacenar datos de comunicación entre procesos.
Método de comunicación entre hilos.
  1. Sección crítica: acceda a recursos públicos o un fragmento de código a través de serialización multiproceso, que es rápida y adecuada para controlar el acceso a datos;
  2. Mutex Synchronized/ Lock: utiliza un mecanismo de objeto mutuamente excluyente, solo el subproceso propietario del objeto mutuamente excluyente tiene permiso para acceder a recursos públicos. Debido a que solo hay un objeto mutex, se puede garantizar que varios subprocesos no accederán a los recursos públicos al mismo tiempo.
  3. Semáforo Semphare: Diseñado para controlar un número limitado de recursos del usuario. Permite que varios subprocesos accedan al mismo recurso al mismo tiempo. Sin embargo, generalmente es necesario limitar el número máximo de subprocesos que pueden acceder a este recurso al mismo tiempo.
  4. Evento (señal), Wait/ Notify: mantiene la sincronización de múltiples subprocesos a través de operaciones de notificación y también puede implementar convenientemente operaciones de comparación de prioridad de múltiples subprocesos y métodos de comunicación entre procesos:

6. Cómo garantizar la seguridad de los subprocesos de iOS

Referencia: ¿Qué tecnologías pueden garantizar la seguridad de los subprocesos en iOS?
Pregunta: Un recurso puede ser compartido por varios subprocesos, es decir, varios subprocesos pueden acceder al mismo recurso, como por ejemplo varios subprocesos que acceden al mismo objeto, a la misma variable y al mismo archivo. Cuando varios subprocesos acceden al mismo recurso, es fácil causar confusión en los datos y problemas de seguridad de los datos. En este punto, necesitamos usar bloqueos de subprocesos para resolverlo.

Métodos de seguridad de datos de subprocesos:

  1. operación atómica natómica : utilice el principio de atomiccontrol atómico de subprocesos múltiples para agregar bloqueos pero no bloqueos.atomicsettergetter
  2. Utilice GCD para implementar operaciones atómicas: agregue una cola de sincronización al método de establecimiento y al método de obtención de un campo;
- (void)setCount:(NSInteger)newcount
{
    
    
    dispatch_sync(_synQueue, ^{
    
    
         count = newcount;
    });
}
- (NSInteger)count
{
    
    
     __block NSInteger localCount;
     dispatch_sync(_synQueue, ^{
    
    
          localCount = count;
     });
     return localCount;
}
  • Los bloqueos Mutex pueden prevenir eficazmente los problemas de seguridad de los datos causados ​​por el robo de recursos por subprocesos múltiples, pero requieren una gran cantidad de recursos de CPU.
  1. Bloqueo mutex: utilice un bloqueo mutex para garantizar que solo un subproceso acceda a los recursos compartidos al mismo tiempo. Por ejemplo @Crear synchronizedun bloqueo mutex
@synchronized (self) {
    
    
    // 访问共享资源的代码
}

  1. Bloqueo de giro: Bloqueo de giro (Spin Lock): El bloqueo de giro es un bloqueo de espera ocupado que intenta continuamente adquirir el bloqueo hasta que lo logra. En Objective-C, os_unfair_locklos bloqueos de giro se pueden crear usando .
  2. Semáforo ( Semaphore): un semáforo es un contador que se utiliza para controlar la cantidad de subprocesos que acceden a un recurso al mismo tiempo.
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 访问共享资源的代码
dispatch_semaphore_signal(semaphore);

  1. Cola en serie: Cola en serie (Serial Queue): el uso de una cola en serie puede garantizar que las tareas se ejecuten en orden, evitando así que varios subprocesos accedan a recursos compartidos al mismo tiempo. GCD(Grand Central Dispatch)Las colas en serie se pueden crear usando .
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
    
    
    // 访问共享资源的代码
});

Supongo que te gusta

Origin blog.csdn.net/weixin_61639290/article/details/132011165
Recomendado
Clasificación