GCD Program Guide API - shujucn/ioscoding GitHub Wiki

Dispatch Queue

“开发者要做的只是定义想要执行的任务,并追加到适当的Dispatch Queque中”,用代码表述如下:

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

Dispatch Queue在上述代码 就是执行处理等待的队列,开发者可通过下面的代码创建想要执行的队列。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

Dispatch Queque的分类

  1. Serial Dispatch queue:又叫private dispatch queues,同时只执行一个任务。一个Serial queue只占用一个线程,常用于同步访问特定的资源或数据。当你创建多个Serialqueue时,虽然各自是同步,但serial queue之间是并发执行。
  2. Concurrent Dispatch queue:又叫global dispatch queue,可以并发的执行多个任务,但执行完成顺序是随机的。系统提供四个全局并发队列,这四个队列有这对应的优先级,用户是不能够创建全局队列的,只能获取。
  3. Main Dispatch queue:全局可用的serial queue,在应用程序主线程上执行任务。

dispatch_queue_creat

通过dispatch_queue_creat函数来生产可执行的Dispatch Queue。 该函数有两个参数,第一个自定义的队列名,第二个参数是队列类型,默认NULL或者DISPATCH_QUEUE_SERIAL的是串行,参数为DISPATCH_QUEUE_CONCURRENT为并行队列。

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

队列优先级

可以通过dipatch_queue_attr_make_with_qos_class或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

可以设置优先级,也可以设置队列层级体系,比如让多个串行和并行队列在统一一个串行队列里串行执行。

//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];
});

Main Dispatch Queue/Global Dispatch Queue

除了通过上文中的create函数获取Queue,GCD也提供了如下系统的标准queue。

Main Dispatch Queue

在主线程钟执行的Queue,只在主线程执行,属于Serial queue。主要用于界面更新等一些必须在主线程中执行的处理。

queue = dispatch_get_main_queue();

Global Dispatch Queue

GCD提供的所有任务都可以使用的Concurrent Queue。没有必要通过dispatch queue_creat函数逐一添加。Global Dispatch Queue有如下四个执行优先级:

  • 高优先级(High Priority)
  • 默认优先级(Default Priority)
  • 低优先级(Background Priority)
  • 后台优先级(Low Priority)

创建Main Queue和四种不同优先级的 Global Queue:

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 有下面这5种队列:

  1. QOS_CLASS_USER_INTERACTIVE:userinteractive等级表示任务需要被立即执行提供好的体验,用来更新UI,响应事件 等。这个等级最好保持小规模。
  2. QOS_CLASS_USER_INITIATED:user initiated等级表示任务由UI发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。
  3. QOS_CLASS_UTILITY:utility等级表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。
  4. QOS_CLASS_BACKGROUND:background等级表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。
  5. MAIN QUEUE:主队列,用于更新UI
  • 主队列:队列中有任务完成需要更新UI时,dispatch_after在这种类型中使用。
  • 并发队列:用来执行与UI无关的后台任务,dispatch_sync放在这里,方便等待任务完成进行后续处理或和dispatch barrier同步。dispatch groups放在这里也不错。
  • 自定义顺序队列:顺序执行后台任务并追踪它时。这样做同时只有一个任务在执行可以防止资源竞争。dipatch barriers解决读写锁问题的放在这里处理。dispatch groups也是放在这里。
//可以使用下面的方法简化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

dispatch_once_t要是全局或static变量,保证dispatch_once_t只有一份实例。即保证dispatch_once block 中的代码只执行一次。通常用于单例的实例化。

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

dispatch_set_target_queue

dispatch_creat_queue 生成的dispatch queue 不管是serial queue 还是concurrent queue,都适用默认优先级global dispatch queue 相同执行优先级的线程。使用dispatch_set_target_queue变更执行优先级。 指定要变更优先级的dispatch queue作为dispatch_set_target_queue函数的参数,不仅可以变更dispatch queue的执行优先级,还可以作为dispatch queue 的执行阶层。如果在多个serial dispatch queue 中用dispatch_set_target_queue函数指定某目标为某一个serial dispatch queue,那么原本应并行执行的有多个serial dispatch queue ,在目标serial dispatch queue上只能同时执行一个处理。

//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的优先级一样

在必须将不可执行的处理追加到多个dispatch queue 中时,如果使用dispatch_set_target_queue函数目标指定为某一个serial dispatch queue就可以防治并行处理。 可以设置优先级,也可以设置队列层级体系,比如让多个串行和并行队列在统一一个串行队列里串行执行,如下

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

dispatch_after

dispatch_after执行在指定时间追加处理到dispatch queue,达到延后执行代码的效果。 dispatch_after只是延时提交block,不是延时立刻执行。

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

例子中的dispatch time的参数,可以先看看函数原型

dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );

第一个参数为DISPATCH_TIME_NOW表示当前。第二个参数的delta表示纳秒,一秒对应的纳秒为1000000000,系统提供了一些宏来简化

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

这样如果要表示一秒就可以这样写

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执行延时提交dispatch_time_t类型的值,该值使用dispatch_time或者dispatch_walltime函数生成。

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

ull 是c语言的数值字面量,是显示表明类型时使用的字符串。 表示(unsighned long long)

  • dispatch_time 通常用于计算相对时间,
  • dispatch_walltime则用于计算绝对时间

下面的代码用来指定date,这一绝对时间的情况:

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

Dispatch Barrier确保提交的闭包是指定队列中在特定时段唯一在执行的一个。在所有先于Dispatch Barrier的任务都完成的情况下这个闭包才开始执行。轮到这个闭包时barrier会执行这个闭包并且确保队列在此过程不会执行其它任务。闭包完成后队列恢复。需要注意dispatch_barrier_async只在自己创建的队列上有这种作用,在全局并发队列和串行队列上,效果和dispatch_sync一样。 dispatch_barrier_async使用Barrier Task方法Dispatch Barrier解决多线程并发读写同一个资源发生死锁。

//使用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
}
//这样读写问题都解决了。都用异步处理避免死锁,
//异步的缺点在于调试不方便,但是比起同步容易产生死锁这个副作用还算小的。

dispatch_group

在追加到Dispatch Queue中的多个任务处理完毕之后想执行结束处理,这种需求会经常出现。如果只是使用一个Serial Dispatch Queue(串行队列)时,只要将想执行的处理全部追加到该串行队列中并在最后追加结束处理即可,但是在使用Concurrent Queue 时,可能会同时使用多个Dispatch Queue时,源代码就会变得很复杂。

在这种情况下,就可以使用Dispatch Group。

 *
    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");
    });

三个queue执行好的顺序不定,当三个queue里面的任务执行完之后,调用dispatch_group_notify,执行dispatch_group_notify queue中的任务。

另外,再Dispatch Group中也可以使用dispatch_group_wait函数仅等待全部处理执行结束。

*
    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);
    

dispatch_sync

同步执行block,函数不返回,一直等到block执行完毕。编译器会根据实际情况优化代码,所以有时候你会发现block其实还在当前线程上执行,并没用产生新线程。同步队列,dispatch_sync 函数不会立即返回,及阻塞当前线程,等待 block同步执行完成。

一旦调用dispatch_sync函数,那么指定的处理执行结束之前,该函数不会返回,dispatch_sync可以简化函数,也可以说是简易版的dispatch_group_wait。

实际编程经验告诉我们,尽可能避免使用dispatch_sync,嵌套使用时还容易引起程序死锁。

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

   dispatch_sync(queue1, ^{

      dispatch_sync(queue1, ^{

    ......

  });

  ......

 });


dispatch_apply

dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API,该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等到全部的处理执行结束.

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

该源代码的执行结果:

4 3 5 0 2 1 7 6 9 8 done

输出 结果中最后的done必定在最后的位置上。这是因为dispatch_apply函数会等待全部处理执行结束

  • 第一个参数为重复次数
  • 第二个参数为追加对象的Dispatch Queue
  • 第三个参数为追加的处理。

另外,由于dispatch_apply函数与dispatch_sync函数相同,会等待处理执行结束,因此推荐在dispatch_async函数中非同步地执行dispatch_apply函数

*
    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");
        });
    });

dispatch_suspend / dispatch_resume

当追加大量处理到Dispatch Queue时,在追加处理的过程中,有时希望不执行已追加的处理。例如演算结果被Block截获时,一些处理会对这个演算结果造成影响。 在这种情况下,只要挂起Dispatch Queue即可。当可以执行时再恢复。 dispatch_suspend函数挂起指定的Dispatch Queue。

dispatch_suspend(queue);

dispatch_resume函数恢复指定的Dispatch Queue。

dispatch_resume(queue);

这些函数对已经执行的处理没有影响。挂起后,追加到Dispatch Queue中但尚未执行的处理在此之后停止执行。而恢复则使这些处理能够继续执行。

dispatch semaphore

dispatch_semaphore_t 类似信号量,可以用来控制访问某一资源访问数量。 使用过程:

  • 先创建一个Dispatch Semaphore对象,用整数值表示资源的可用数量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  • 在每个任务中,调用dispatch_semaphore_wait来等待 获得资源就可以进行操作
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  • 操作完后调用dispatch_semaphore_signal来释放资源
dispatch_semaphore_signal(semaphore);

下面是一个简单的通过semaphore保证线程数据安全的例子:

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

这样我们一样可以保证,线程的数据安全。

Dispatch I/O

在读取较大的文件时,如果将文件分成合适的大小并使用 Global Dispatch Queue 并列读取的话,应该会比一般的读取速度快不少。 在 GCD 当中能实现这一功能的就是 Dispatch I/O 和 Dispatch Data。 通过Dispatch I/O读写文件时,使用Global Dispatch Queue将1个文件按某个大小read/write。

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 字节*/ });  

请看下面苹果中使用Dispatch I/O 和 Dispatch Data的例子。下面的代码摘自 Apple System Log API里的源代码

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);  
        }  
    });  
}  
  • 创建一个调度I / O通道
  1. 创建一个调度I / O通道,并将其与指定的文件描述符关联。
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. 创建一个具有关联路径名的调度I / O通道。
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. 从现有的信道创建一个新的调度I / O信道。
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));
  • 读写操作
  1. 在指定的信道上调度异步读操作。
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的错误代码,它意味着该通道达到了文件的结尾。
    
-----------------------------------
      

dispatch_io_read 函数使用 Global Dispatch Queue 开始并列读取,每当各个分割的文件块读取结束时,将含有 Dispatch Data 传递给 dispatch 函数指定的读取结束时回调用的 Block。

  1. 为指定的信道调度一个异步写操作。
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);

该函数将指定的数据并提交io_handler块队列在操作的进度报告。如果处理程序的所做的参数设置为“不”,则意味着只有部分数据被写入。如果所做的参数设置为是的,这意味着写操作完成,处理程序将不会再次提交。如果操作成功,则处理程序的错误参数设置为0。

  • 设置一次读取的大小
  1. 设置一次读取的最大字节
void dispatch_io_set_high_water( dispatch_io_t channel, size_t high_water);
  1. 设置一次读取的最小字节
void dispatch_io_set_low_water( dispatch_io_t channel, size_t low_water);
⚠️ **GitHub.com Fallback** ⚠️