Explicación detallada de API de cola de despacho

Cola de despacho

"Todo lo que el desarrollador debe hacer es definir las tareas que desea ejecutar y agregarlas a la cola de despacho adecuada", expresado en código de la siguiente manera:

    dispatch_async(queue, ^{
       /*
        * 想要执行的任务Code
        */
    });

La cola de envío en el código anterior es la cola de espera para la ejecución y el procesamiento. Los desarrolladores pueden crear la cola que desean ejecutar a través del siguiente código.

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

Clasificación de la cola de despacho

  1. Cola de envío en serie : también conocidas como colas de envío privadas , solo se ejecuta una tarea al mismo tiempo. Una cola en serie ocupa solo un subproceso y, a menudo, se usa para sincronizar el acceso a recursos o datos específicos. Cuando crea varias colas en serie, aunque cada una está sincronizada, las colas en serie se ejecutan simultáneamente.
  2. Cola de envío concurrente : también llamada cola de envío global, se pueden ejecutar varias tareas al mismo tiempo, pero el orden de ejecución es aleatorio. El sistema proporciona cuatro colas globales concurrentes. Estas cuatro colas tienen prioridades correspondientes. Los usuarios no pueden crear colas globales, solo pueden obtenerlas.
  3. Cola de envío principal : una cola en serie disponible globalmente que ejecuta tareas en el subproceso principal de la aplicación.

dispatch_queue_creat

Produzca una cola de envío ejecutable a través de la función dispatch_queue_creat.
Esta función tiene dos parámetros, el primer nombre de cola personalizado, el segundo parámetro es el tipo de cola, el valor predeterminado NULL o DISPATCH_QUEUE_SERIAL es serie y el parámetro DISPATCH_QUEUE_CONCURRENT es cola paralela.

dispatch_queue_t queueCon, queueSerial;
queueCon = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
queueSerial = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);

prioridad de cola

La prioridad de la cola se puede establecer mediante el método dispatch_queue_attr_make_with_qos_class o dispatch_set_target_queue.

dipatch_queue_attr_make_with_qos_class

 * Returns an attribute value which may be provided to dispatch_queue_create()
 * or dispatch_queue_create_with_target(), in order to assign a QOS class and
 * relative priority to the queue.
 *
 * @discussion
 * When specified in this manner, the QOS class and relative priority take
 * precedence over those inherited from the dispatch queue's target queue (if
 * any) as long that does not result in a lower QOS class and relative priority.
 *
 * The global queue priorities map to the following QOS classes:
 *  - DISPATCH_QUEUE_PRIORITY_HIGH:         QOS_CLASS_USER_INITIATED
 *  - DISPATCH_QUEUE_PRIORITY_DEFAULT:      QOS_CLASS_DEFAULT
 *  - DISPATCH_QUEUE_PRIORITY_LOW:          QOS_CLASS_UTILITY
 *  - DISPATCH_QUEUE_PRIORITY_BACKGROUND:   QOS_CLASS_BACKGROUND
 *
 * Example:
 * <code>
 *  dispatch_queue_t queue;
 *  dispatch_queue_attr_t attr;
 *  attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
 *          QOS_CLASS_UTILITY, 0);
 *  queue = dispatch_queue_create("com.example.myqueue", attr);
 * </code>
  * @param attr
 * A queue attribute value to be combined with the QOS class, or NULL.
 *
 * @param qos_class
 * A QOS class value:
 *  - QOS_CLASS_USER_INTERACTIVE
 *  - QOS_CLASS_USER_INITIATED
 *  - QOS_CLASS_DEFAULT
 *  - QOS_CLASS_UTILITY
 *  - QOS_CLASS_BACKGROUND
 * Passing any other value results in NULL being returned.
 *
 * @param relative_priority
 * A relative priority within the QOS class. This value is a negative
 * offset from the maximum supported scheduler priority for the given class.
 * Passing a value greater than zero or less than QOS_MIN_RELATIVE_PRIORITY
 * results in NULL being returned.
 *
 * @return
 * Returns an attribute value which may be provided to dispatch_queue_create()
 * and dispatch_queue_create_with_target(), or NULL if an invalid QOS class was
 * requested.
 * The new value combines the attributes specified by the 'attr' parameter and
 * the new QOS class and relative priority.
 */

dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, -1);
dispatch_queue_t queue = dispatch_queue_create("com.starming.gcddemo.qosqueue", attr);

dispatch_set_target_queue

Puede establecer la prioridad y también puede establecer la jerarquía de la cola, como permitir que varias colas en serie y en paralelo se ejecuten en serie en la misma cola en serie.

//dispatch_set_target_queue
dispatch_queue_t queue = dispatch_queue_create("com.starming.gcddemo.settargetqueue",NULL); //需要设置优先级的queue
dispatch_queue_t referQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //参考优先级
//设置queue和referQueue的优先级一样
dispatch_set_target_queue(queue, referQueue); 

//让多个串行和并行队列在统一一个串行队列里串行执行
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t firstQueue = dispatch_queue_create("com.starming.gcddemo.firstqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t secondQueue = dispatch_queue_create("com.starming.gcddemo.secondqueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_set_target_queue(firstQueue, serialQueue);
dispatch_set_target_queue(secondQueue, serialQueue);

dispatch_async(firstQueue, ^{
    NSLog(@"1");
    [NSThread sleepForTimeInterval:3.f];
});
dispatch_async(secondQueue, ^{
    NSLog(@"2");
    [NSThread sleepForTimeInterval:2.f];
});
dispatch_async(secondQueue, ^{
    NSLog(@"3");
    [NSThread sleepForTimeInterval:1.f];
});

Cola de despacho principal/Cola de despacho global

Además de obtener cola a través de la función de creación anterior, GCD también proporciona colas estándar para los siguientes sistemas.

Cola de despacho principal

La Cola ejecutada en el reloj del subproceso principal solo se ejecuta en el subproceso principal y pertenece a la cola Serial. Se utiliza principalmente para algunos procesos que deben realizarse en el subproceso principal, como la actualización de la interfaz.

queue = dispatch_get_main_queue();

Cola de despacho global

Cola simultánea que pueden utilizar todas las tareas proporcionadas por GCD. No es necesario agregar uno por uno a través de la función dispatch queue_creat. La cola de envío global tiene las cuatro prioridades de ejecución siguientes:
- Prioridad alta
- Prioridad predeterminada
- Prioridad en segundo plano
- Prioridad baja

Cree cola principal y cola global con cuatro prioridades diferentes:

dispatch_get_main_queue();// Main Queue
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

dispatch_queue tiene las siguientes cinco colas:

  1. QOS_CLASS_USER_INTERACTIVE: el nivel interactivo del usuario indica que la tarea debe ejecutarse de inmediato para brindar una buena experiencia, se utiliza para actualizar la interfaz de usuario, responder a eventos, etc. Es mejor mantener esta clase pequeña.
  2. QOS_CLASS_USER_INITIATED: el nivel iniciado por el usuario indica que la interfaz de usuario ejecuta la tarea de forma asíncrona. El escenario aplicable es cuando se requieren resultados oportunos mientras se continúa interactuando.
  3. QOS_CLASS_UTILITY: la clase de utilidad indica tareas de ejecución prolongada con indicadores de progreso visibles para el usuario. A menudo se usa para cálculo, E/S, red, llenado continuo de datos y otras tareas. Esta tarea ahorra energía.
  4. QOS_CLASS_BACKGROUND: el nivel de fondo indica tareas que el usuario no notará, utilícelo para manejar la precarga o tareas que no requieren la interacción del usuario y no son sensibles al tiempo.
  5. COLA PRINCIPAL: La cola principal, utilizada para actualizar la interfaz de usuario

    • Cola principal : dispatch_after se usa en este tipo cuando hay tareas en la cola que necesitan actualizar la interfaz de usuario.
    • Cola concurrente : se utiliza para ejecutar tareas en segundo plano que no tienen nada que ver con la UI, aquí se coloca dispatch_sync, por lo que es conveniente esperar a que la tarea se complete para su procesamiento posterior o para sincronizar con la barrera de despacho. También es bueno poner grupos de despacho aquí.
    • Cola secuencial personalizada : al ejecutar tareas en segundo plano secuencialmente y realizar un seguimiento. Hacerlo evita la contención de recursos al tener solo una tarea ejecutándose a la vez. Las barreras de distribución para resolver el problema de bloqueo de lectura-escritura se tratan aquí. Los grupos de envío también se colocan aquí.
//可以使用下面的方法简化QoS等级参数的写法:

var GlobalMainQueue: dispatch_queue_t {
     return dispatch_get_main_queue()
}
var GlobalUserInteractiveQueue: dispatch_queue_t {
     return dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.value), 0)
}
var GlobalUserInitiatedQueue: dispatch_queue_t {
     return dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)
}
var GlobalUtilityQueue: dispatch_queue_t {
     return dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0)
}
var GlobalBackgroundQueue: dispatch_queue_t {
     return dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0)
}

//使用起来就是这样,易读而且容易看出在使用哪个队列
dispatch_async(GlobalUserInitiatedQueue) {
     let overlayImage = self.faceOverlayImageFromImage(self.image)
     dispatch_async(GlobalMainQueue) {
          self.fadeInNewImage(overlayImage)
     }
}

dispatch_once

Si dispatch_once_t es una variable global o estática, asegúrese de que solo haya una instancia de dispatch_once_t. Es decir, se garantiza que el código en el bloque dispatch_once se ejecutará solo una vez. Normalmente se utiliza para la creación de instancias de singletons.

+(instancetype)sharedNKPlayerTool{
  static dispatch_once_t once;
  dispatch_once(&once, ^{
      tool = [[NKPlayerTool alloc]init];
  });
  return tool;
}

dispatch_set_target_queue

Ya sea que la cola de envío generada por dispatch_creat_queue sea una cola en serie o una cola concurrente, se aplican subprocesos con la misma prioridad de ejecución que la cola de envío global de prioridad predeterminada. Use dispatch_set_target_queue para cambiar la prioridad de ejecución.
Especifique la cola de envío cuya prioridad se cambiará como un parámetro de la función dispatch_set_target_queue, que no solo puede cambiar la prioridad de ejecución de la cola de envío, sino que también sirve como nivel de ejecución de la cola de envío. Si usa la función dispatch_set_target_queue para especificar un destino como una cola de envío en serie en varias colas de envío en serie, entonces hay varias colas de envío en serie que deben ejecutarse en paralelo, y solo se puede ejecutar un proceso en la cola de envío en serie de destino al mismo tiempo.

//dipatch_queue_attr_make_with_qos_class
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, -1);
dispatch_queue_t queue = dispatch_queue_create("com.gcddemo.qosqueue", attr);

//dispatch_set_target_queue
dispatch_queue_t queue = dispatch_queue_create("com.gcddemo.settargetqueue",NULL); //需要设置优先级的queue
dispatch_queue_t referQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //参考优先级
dispatch_set_target_queue(queue, referQueue); //设置queue和referQueue的优先级一样

Cuando se debe agregar procesamiento no ejecutable a varias colas de envío, si el objetivo de la función dispatch_set_target_queue se especifica como una cola de envío en serie, se puede evitar el procesamiento en paralelo.
Puede establecer la prioridad y también puede establecer la jerarquía de la cola, como permitir que varias colas en serie y paralelas se ejecuten en serie en la misma cola en serie, de la siguiente manera

dispatch_queue_t serialQueue = dispatch_queue_create("com.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t firstQueue = dispatch_queue_create("com.gcddemo.firstqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t secondQueue = dispatch_queue_create("com.gcddemo.secondqueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_set_target_queue(firstQueue, serialQueue);
dispatch_set_target_queue(secondQueue, serialQueue);

dispatch_async(firstQueue, ^{
    NSLog(@"1");
    [NSThread sleepForTimeInterval:3.f];
});
dispatch_async(secondQueue, ^{
    NSLog(@"2");
    [NSThread sleepForTimeInterval:2.f];
});
dispatch_async(secondQueue, ^{
    NSLog(@"3");
    [NSThread sleepForTimeInterval:1.f];
});

despacho_después

la ejecución dispatch_after agrega procesamiento a la cola de envío en un momento específico para lograr el efecto de retrasar la ejecución del código.
dispatch_after solo retrasa el envío del bloque, no retrasa la ejecución inmediatamente.

double delayInSeconds = 2.0;
     dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delayInSeconds * NSEC_PER_SEC));
     dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
          [self bar];
     });

Para los parámetros de tiempo de despacho en el ejemplo, primero puede ver el prototipo de función

dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );

El primer parámetro es DISPATCH_TIME_NOW, que significa actual. El delta del segundo parámetro representa nanosegundos, y un segundo corresponde a 1000000000 nanosegundos. El sistema proporciona algunas macros para simplificar

#define NSEC_PER_SEC 1000000000ull //每秒有多少纳秒
#define USEC_PER_SEC 1000000ull    //每秒有多少毫秒
#define NSEC_PER_USEC 1000ull      //每毫秒有多少纳秒

De esta forma, si quieres expresar un segundo, puedes escribirlo así

dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);

dispatch_after realiza el envío retrasado de valores de tipo dispatch_time_t, que se generan utilizando las funciones dispatch_time o dispatch_walltime.

// 得到从现在开始10秒后的dispatch_time_t,
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 10ull*NSEC_PER_SEC)

ull es un literal numérico en el lenguaje C y es una cadena de caracteres que se usa cuando se muestra el tipo.
Indica (unsighed long long)
- dispatch_time generalmente se usa para calcular el tiempo relativo,
- dispatch_walltime se usa para calcular el tiempo absoluto

El siguiente código se utiliza para especificar la fecha, en el caso de la hora absoluta:

NSTimeInterval interval;
 double second, subsecond;
 struct timespec time;
 dispatch_time_t walltime;
 interval = [date timeIntervalSince1970];
 time.tv_sec = second;
 time.tv_nsec = subsecond * NSEC_PER_SEC;
 walltime = dispatch_walltime(&time, 0)

dispatch_barrier_async

La barrera de despacho garantiza que el cierre enviado sea el único en la cola especificada que se está ejecutando en un momento específico. Este cierre no comenzará a ejecutarse hasta que se hayan completado todas las tareas previas a la Barrera de despacho. Cuando es el turno del cierre, la barrera ejecuta el cierre y asegura que la cola no realice otras tareas durante el proceso. La cola se reanuda después de que se completa el cierre. Cabe señalar que dispatch_barrier_async solo tiene este efecto en la cola creada por él mismo. En la cola concurrente global y la cola en serie, el efecto es el mismo que dispatch_sync.
dispatch_barrier_async utiliza el método Dispatch Barrier de Tarea de barrera para resolver el punto muerto que se produce cuando varios subprocesos leen y escriben el mismo recurso al mismo tiempo.

//使用dispatch_queue_create初始化一个并发队列。第一个参数遵循反向DNS命名习惯,方便描述,第二个参数是指出是并发还是顺序。
private let concurrentPhotoQueue = dispatch_queue_create(
"com.raywenderlich.GooglyPuff.photoQueue", DISPATCH_QUEUE_CONCURRENT)

func addPhoto(photo: Photo) {
     dispatch_barrier_async(concurrentPhotoQueue) { 
        // 将写操作加入到自定义的队列。开始执行时这个就是队列中唯一的一个在执行的任务。
        self._photos.append(photo) 
        // barrier能够保障不会和其他任务同时进行。
        dispatch_async(GlobalMainQueue) { 
        // 涉及到UI所以这个通知应该在主线程中,所以分派另一个异步任务到主队列中。
               self.postContentAddedNotification()
          }
     }
}

//上面是解决了写可能发生死锁,下面是使用dispatch_sync解决读时可能会发生的死锁。
var photos: [Photo] {
     var photosCopy: [Photo]!
     dispatch_sync(concurrentPhotoQueue) { 
     //同步调度到concurrentPhotoQueue队列执行读操作
        photosCopy = self._photos // 保存
     }
     return photosCopy
}
//这样读写问题都解决了。都用异步处理避免死锁,
//异步的缺点在于调试不方便,但是比起同步容易产生死锁这个副作用还算小的。

grupo_despacho

Después de que se procesen las múltiples tareas agregadas a la Cola de envío, desea realizar el procesamiento final, lo que ocurre con frecuencia. Si solo usa una cola de envío en serie (cola en serie), solo necesita agregar todo el procesamiento que desea ejecutar a la cola en serie y finalizar el procesamiento al final. Sin embargo, cuando usa la cola concurrente, puede usar varias colas de envío al mismo tiempo, y el código fuente se volverá muy complicado.

En este caso, se puede utilizar Grupo de despacho.

 *
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.gcd-group.www", DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_async(group, queue, ^{
        NSLog(@"1");
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"2");
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"3");
    });

    dispatch_group_notify(group, queue, ^{
        NSLog(@"done");
    });

El orden de ejecución de las tres colas es incierto Después de ejecutar las tareas en las tres colas, se llama a dispatch_group_notify para ejecutar las tareas en la cola dispatch_group_notify.

Además, la función dispatch_group_wait también se puede utilizar en el grupo de despacho para esperar solo a que finalicen todas las ejecuciones de procesamiento.

*
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.gcd-group.www", DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_async(group, queue, ^{
        NSLog(@"1");
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"2");
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"3");
    });
    // DISPATCH_TIME_FOREVER, 永久等待,知道Group处理结束。
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

despacho_sincronización

El bloque se ejecuta sincrónicamente, la función no regresa y espera hasta que se ejecuta el bloque. El compilador optimizará el código de acuerdo con la situación real, por lo que a veces encontrará que el bloque aún se está ejecutando en el subproceso actual y es inútil generar un nuevo subproceso. Cola síncrona, la función dispatch_sync no regresará inmediatamente y bloqueará el hilo actual, esperando que se complete la ejecución síncrona del bloque.

Una vez que se llama a la función dispatch_sync, la función no regresará hasta que se ejecute el procesamiento especificado. dispatch_sync puede simplificar la función, y también se puede decir que es una versión simple de dispatch_group_wait.

La experiencia de programación real nos dice que debemos evitar usar dispatch_sync tanto como sea posible, y es fácil provocar un punto muerto del programa cuando está anidado.

如果queue1是一个串行队列的话,这段代码立即产生死锁:

   dispatch_sync(queue1, ^{

      dispatch_sync(queue1, ^{

    ......

  });

  ......

 });

despacho_aplicar

La función dispatch_apply es la API asociada de la función dispatch_sync y el grupo de despacho.Esta función agrega el bloque especificado a la cola de despacho especificada de acuerdo con el número de veces especificado y espera hasta que se completan todas las ejecuciones de procesamiento.

dispatch_queue_t queue = dispatch_get_global_queu(0, 0);
dispatch_apply(10, queue, ^(size_t index){
NSLog(@"%zu", index);
});
NSLog(@"done");

El resultado de la ejecución de este código fuente:

4 3 5 0 2 1 7 6 9 8 done

El último hecho en la salida debe estar en la última posición. Esto se debe a que la función dispatch_apply espera a que finalice toda la ejecución del procesamiento.

  • El primer parámetro es el número de repeticiones.
  • El segundo parámetro es la cola de envío del objeto adjunto.
  • El tercer parámetro es el procesamiento adicional.

Además, dado que la función dispatch_apply es la misma que la función dispatch_sync, esperará a que finalice la ejecución del procesamiento, por lo que se recomienda ejecutar la función dispatch_apply de forma asíncrona en la función dispatch_async

*
    NSArray *array = @[@"1", @"2", @"3", @"4", @"5", @"6"];
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        dispatch_apply([array count], queue, ^(size_t index) {
            NSLog(@"%zu : %@", index, [array objectAtIndex:index]);
        });

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"currentThread = %@", [NSThread currentThread]);
            NSLog(@"done");
        });
    });

despacho_suspender / despacho_resume

Al agregar una gran cantidad de procesamiento a la Cola de despacho, puede ser conveniente no ejecutar el procesamiento agregado durante el procesamiento adicional. Por ejemplo, cuando el bloque intercepta el resultado del cálculo, algún procesamiento afectará el resultado del cálculo.
En este caso, simplemente suspenda la cola de despacho. Reanudar cuando se pueda ejecutar.
La función dispatch_suspend suspende la cola de despacho especificada.

dispatch_suspend(queue);

La función dispatch_resume reanuda la cola de despacho especificada.

dispatch_resume(queue);

Estas funciones no tienen ningún efecto sobre el procesamiento ya realizado. Después de la suspensión, el procesamiento que se agregó a la Cola de envío pero que aún no se ha ejecutado detiene la ejecución después de eso. La recuperación permite que estos procesos continúen.

semáforo de despacho

dispatch_semaphore_t es similar a un semáforo, que se puede utilizar para controlar el número de accesos a un determinado recurso.
Proceso de uso:
- Primero cree un objeto Dispatch Semaphore, usando un valor entero para representar la cantidad de recursos disponibles

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  • En cada tarea, llame a dispatch_semaphore_wait para esperar a que
    se operen los recursos
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  • Después de la operación, llame a dispatch_semaphore_signal para liberar recursos
dispatch_semaphore_signal(semaphore);

El siguiente es un ejemplo simple de cómo garantizar la seguridad de los datos del subproceso a través del semáforo:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    __block NSString *strTest = @"test";

    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        if ([strTest isEqualToString:@"test"]) {
            NSLog(@"--%@--1-", strTest);
            [NSThread sleepForTimeInterval:1];
            if ([strTest isEqualToString:@"test"]) {
                [NSThread sleepForTimeInterval:1];
                NSLog(@"--%@--2-", strTest);
            } else {
                NSLog(@"====changed===");
            }
        }
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"--%@--3-", strTest);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        strTest = @"modify";
        NSLog(@"--%@--4-", strTest);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"--%@--5-", strTest);
        dispatch_semaphore_signal(semaphore);
    });

De esta manera, también podemos garantizar la seguridad de los datos del hilo.

E/S de despacho

Al leer un archivo grande, si el archivo se divide en tamaños apropiados y se lee en paralelo usando la cola de envío global, debería ser mucho más rápido que la velocidad de lectura general. Lo que puede realizar esta función en GCD es Dispatch I/O y Dispatch Data.
Al leer y escribir archivos a través de Dispatch I/O, use Global Dispatch Queue para leer/escribir un archivo con un tamaño determinado.

dispatch_async(queue, ^{ /* 读取  0     ~ 8080  字节*/ });  
dispatch_async(queue, ^{ /* 读取  8081  ~ 16383 字节*/ });  
dispatch_async(queue, ^{ /* 读取  16384 ~ 24575 字节*/ });  
dispatch_async(queue, ^{ /* 读取  24576 ~ 32767 字节*/ });  
dispatch_async(queue, ^{ /* 读取  32768 ~ 40959 字节*/ });  

Consulte un ejemplo del uso de E/S de envío y datos de envío en Apple a continuación. El siguiente código se toma del código fuente en la API de registro del sistema de Apple

static int  
_asl_auxiliary(aslmsg msg, const charchar *title, const charchar *uti, const charchar *url, intint *out_fd)  
{  
    asl_msg_t *merged_msg;  
    asl_msg_aux_t aux;  
    asl_msg_aux_0_t aux0;  
    fileport_t fileport;  
    kern_return_t kstatus;  
    uint32_t outlen, newurllen, len, where;  
    int status, fd, fdpair[2];  
    caddr_t out, newurl;  
    dispatch_queue_t pipe_q;  
    dispatch_io_t pipe_channel;  
    dispatch_semaphore_t sem;  
    /* ..... 此处省略若干代码.....*/  

    // 创建串行队列  
    pipe_q = dispatch_queue_create("PipeQ", NULL);  
    // 创建 Dispatch I/O  
    pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){  
        close(fd);  
    });  

    *out_fd = fdpair[1];  

    // 该函数设定一次读取的大小(分割大小)  
    dispatch_io_set_low_water(pipe_channel, SIZE_MAX);  
    //  
    dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){  
        if (err == 0) // err等于0 说明读取无误  
        {  
            // 读取完“单个文件块”的大小  
            size_t len = dispatch_data_get_size(pipedata);  
            if (len > 0)  
            {  
                // 定义一个字节数组bytes  
                const charchar *bytes = NULL;  
                charchar *encoded;  

                dispatch_data_t md = dispatch_data_create_map(pipedata, (const voidvoid **)&bytes, &len);  
                encoded = asl_core_encode_buffer(bytes, len);  
                asl_set((aslmsg)merged_msg, ASL_KEY_AUX_DATA, encoded);  
                free(encoded);  
                _asl_send_message(NULL, merged_msg, -1, NULL);  
                asl_msg_release(merged_msg);  
                dispatch_release(md);  
            }  
        }  

        if (done)  
        {  
            dispatch_semaphore_signal(sem);  
            dispatch_release(pipe_channel);  
            dispatch_release(pipe_q);  
        }  
    });  
}  
  • Crear un canal de E/S programado

    1. Crea un canal de E/S programado y lo asocia con el descriptor de archivo especificado.
dispatch_io_t dispatch_io_create( dispatch_io_type_t type, dispatch_fd_t fd, dispatch_queue_t queue, void (^cleanup_handler)(int error));

-----------------------------------

@Parameters

type  通道类型 (Dispatch I/O Channel Types.)

#define DISPATCH_IO_STREAM 0

读写操作按顺序依次顺序进行。在读或写开始时,操作总是在文件指针位置读或写数据。读和写操作可以在同一个信道上同时进行。

#define DISPATCH_IO_RANDOM 1

随机访问文件。读和写操作可以同时执行这种类型的通道,文件描述符必须是可寻址的。

fd 文件描述符

queue  The dispatch queue

cleanup_handler 发生错误时用来执行处理的 Block

-----------------------------------
  1. Crea un canal de E/S programado con un nombre de ruta asociado.
dispatch_io_t dispatch_io_create_with_path( dispatch_io_type_t type, const char* path, int oflag, mode_t mode, dispatch_queue_t queue, void (^cleanup_handler)(int error));
  1. Cree un nuevo canal de E/S programado a partir de un canal existente.
dispatch_io_t dispatch_io_create_with_io( dispatch_io_type_t type, dispatch_io_t io, dispatch_queue_t queue, void (^cleanup_handler)(int error));
  • operaciones de lectura y escritura

    1. Programa una operación de lectura asíncrona en el canal especificado.
void dispatch_io_read( dispatch_io_t channel, off_t offset, size_t length, dispatch_queue_t queue, dispatch_io_handler_t io_handler);

-----------------------------------
@Parameters

channel 通道

offset 对于DISPATCH_IO_RANDOM 类型的通道,此参数指定要读取的信道的偏移量。

对于DISPATCH_IO_STREAM 类型的通道,此参数将被忽略,数据从当前位置读取。

length 从通道读取的字节数。指定size_max继续读取数据直到达到一个EOF。
      如果处理程序与所做的参数设置为是的,一个空的数据对象,和一个0的错误代码,它意味着该通道达到了文件的结尾。

-----------------------------------

La función dispatch_io_read utiliza la cola de envío global para comenzar a leer en paralelo. Cada vez que finaliza la lectura de cada bloque de archivo dividido, los datos de envío se pasarán al bloque que se devuelve cuando la lectura finaliza especificada por la función de envío.

  1. Programa una operación de escritura asincrónica para el canal especificado.
void dispatch_io_write( dispatch_io_t channel, off_t offset, dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t io_handler);

Esta función toma los datos especificados y los envía a la cola de bloques io_handler para informar sobre el progreso de la operación. Si el parámetro done del controlador se establece en "no", significa que solo se escribe una parte de los datos. Si el parámetro done se establece en sí, significa que la operación de escritura está completa y el controlador no volverá a enviar. Si la operación fue exitosa, el parámetro de error del controlador se establece en 0.

  • Establecer el tamaño de una lectura
    1. Establecer el número máximo de bytes leídos a la vez
void dispatch_io_set_high_water( dispatch_io_t channel, size_t high_water);
  1. Establecer el byte mínimo para leer a la vez
void dispatch_io_set_low_water( dispatch_io_t channel, size_t low_water);

Supongo que te gusta

Origin blog.csdn.net/sinat_15735647/article/details/77982716
Recomendado
Clasificación