iOS并发编程(四)-Dispatch Sources-(待改阅……)

题记: 

“每个人都希望被这个世界温柔对待,但是没有冲突,没有不满,没有人告诉你你其实很一般,很多时候我们就只会温柔的待在原地,很多年。”

未来估计一段时间没法工作了,等“归来”时,我们继续!


Dispatch Source:

1.关于Dispatch Source:

     现代系统通常提供异步接口,允许应用向系统提交请求,然后在系统处理请求时应用可以继续处理自己的事情;GCD正是基于这个基本行为而设计,允许你提交请求,并通过block和dispatch queue报告结果;

     

     dispatch source是基础数据类型,协调特定底层系统事件的处理;

     GCD支持以下dispatch source:

     1)Timer dispatch source:定期产生通知;

     2)Signal dispatch source:UNIX信号到达时产生通知;

     3)Description dispatch source:各种文件和socket操作的通知:

     (1)数据可读;

     (2)数据可写;

     (3)文件在文件系统中被删除、移动、重命名;

     (4)文件元数据信息改变;

     4)Progress dispatch source:进程相关的事件通知:

     (1)当进程退出时;

     (2)当进程发起fork或exec等调用;

     (3)信号被递送到进程;

     5)Mach port dispatch source:Mach相关事件通知;

     6)Custom dispatch source:你自己定义并自己触发;

     

     Dispatch source替代了异步回调函数,来处理系统相关的事件;

     当你配置一个dispatch source时,你指定要检测的事件、dispatch queue、以及处理事件的代码(block或函数);当事件发生时,dispatch source会提交你的block或函数到指定的queue去执行;

     

     和手工提交到queue的任务不同,dispatch source为应用提供连续的事件源;除非你显式地取消,否则dispatch source会一直保留与dispatch queue的关联;只要相应的事件发生,就会提交关联的代码到dispatch queue去执行;

     

     为了防止事件积压到dispatch queue,dispatch source实现了事件合并机制,如果新事件在上一个事件处理器出列并执行之前到达,dispatch source会将新旧事件的数据合并;根据事件类型的不同,合并操作可能会替换旧事件,或者更新旧事件信息;

     

     2.创建Dispatch Source:

     创建dispatch source 需要同时创建事件源和dispatch source 本身;

     事件源是处理事件所需要的native数据结构,例如基于描述符的dispatch source,你需要打开描述符;基于进程的事件,你需要获得目标程序的进程ID;

     

     然后可以如下创建相应的dispatch source:

     1)使用dispatch_source_create函数创建dispatch source;

     2)配置dispatch source:

     (1)为dispatch source设置一个事件处理器;

     (2)对于定时器源,使用dispatch_source_set_timer函数设置定时器信息;

     3)为dispatch source 赋予一个取消处理器(可选);

     4)调用dispatch_resume函数开始处理事件;

     

     由于dispatch source必须进行额外的配置才能被使用,dispatch_source_create函数返回的dispatch source将处于挂起状态;

     此时dispatch source会接受事件,但是不会进行处理;这时候你可以安装事件处理器,并执行额外的配置;

     

     3.编写和安装一个事件处理器:

     你需要定义一个事件处理器来处理事件,可以是函数或block对象,并使用dispatch_source_set_event_handler或dispatch_source_set_event_handler_f安装事件处理器;

     事件到达时,dispatch source会提交你的事件处理器到指定的dispatch queue,由queue执行事件处理器;

     

     事件处理:

     1)事件处理器的代码负责处理所有到达的事件;如果事件处理器已经在queue中并等待处理已经到达的事件,如果此时又来了一个新事件,dispatch source会合并这两个事件;事件处理器通常只能看到最新事件的信息,不过某些类型的dispatch source也能获得已经发生以及合并的事件信息;

     2)如果事件处理器已经开始执行,一个或多个新事件到达,dispatch source会保留这些事件,直到前面的事件处理器完成执行;然后以新事件再次提交处理器到queue;

     

     函数事件处理器有一个context指针指向dispatch source对象,没有返回值;

     block事件处理器没有参数,也没有返回值;

     

     // Block-based event handler

     void (^dispatch_block_t)(void)

     // Function-based event handler

     void (*dispatch_function_t)(void *)

     

     在事件处理器中,你可以从dispatch source中获得事件的信息,函数处理器可以直接使用参数指针,block则必须自己捕获到dispatch source指针,一般block定义时会自动捕获到外部定义的所有变量;

     

     演示-:

     这里的问题和之前设置清理函数时出现的问题一样,设置调用的函数无响应,貌似调用的还是不对?!但是使用block的方式 是可以的;

     

     Block捕获外部变量允许更大的灵活性和动态性;

     当然,block中这些变量默认只是只读的,虽然可以使用__block来修改捕获的变量,但是你最不好不要在事件处理器中这样做,因为Dispatch source异步执行事件处理器,当事件处理器修改原始外部变量时,有可能这些变量已经不存在了;

     

     dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, STDIN_FILENO, 0, queue);//type handle mask queue

     dispatch_source_set_event_handler(source, ^{

     

     });

     dispatch_resume(source);    //开始处理事件

     

     下面是时间处理器能捕获的事件信息:

     1)dispatch_source_get_handle:

     这个函数返回dispatch source管理的底层系统数据类型;

     对于描述符dispatch source,函数返回一个int,表示关联的描述符;

     对于信号dispatch source,函数返回一个int,表示最新事件的信号数值;

     对于进程dispatch source,函数返回一个pid_t数据结构,表示被监控的进程;对于Mach port dispatch source,函数返回一个mach_port_t数据结构;

     对于其他dispatch source,函数返回的值未定义;

     2)dispatch_source_get_data:

     这个函数返回事件关联的所有未决数据;

     对于从文件中读取数据的描述符dispatch source,这个函数返回可以读取的字节数;

     对于向文件中写入数据的描述符dispatch source,如果可以写入,则返回正数值;

     对于监控文件系统活动的描述符dispatch source,函数返回一个常亮,表示发生的事件类型,参考dispatch_source_vnode_flags_t枚举类型;

     对于自定义dispatch source,函数返回从现有数据创建的新数据,以及传递给dispatch_source_merge_data函数的新数据;

     3)dispatch_source_get_mask:

     这个函数返回用来创建dispatch source的事件标志;

     对于进程dispatch source,函数返回dispatch source接收到的事件掩码,参考dispatch_source_proc_flags_t枚举类型;

     对于发送权利的Mach port dispatch source,函数返回期望事件的掩码,参考dispatch_source_mach_send_flags_t枚举类型;

     对于自定义“或”的dispatch source,函数返回用来合并数据值的掩码;

     

     4.安装一个取消处理器:

     取消处理器在 dispatch soruce 释放之前执行清理工作。多数类型的 dispatch source 不需要取消处理器,除非你对 dispatch source 有自定义行为需要在释放时 执行。但是使用 述符或 Mach port 的 dispatch source 必须设置取消处理器,用 来关闭 述符或释放 Mach port。否则可能导致微妙的 bug,这些结构体会被系 统其它部分或你的应用在不经意间重用。

     

     你可以在任何时候安装取消处理器,但通常我们在创建 dispatch source 时就 会安装取消处理器。使用 dispatch_source_set_cancel_handler 或 dispatch_source_set_cancel_handler_f 函数来设置取消处理器。

     

     演示-;

     

/**
 * 四:3、4
 */
static void _dispatch_callback(void * ctxt) {
    
}
-(void)testDispatchSource{
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, STDIN_FILENO, 0, queue);//type handle mask queue
    
    dispatch_source_set_cancel_handler(source, ^{
        //close a file descriptor opened earlier;
    });
    dispatch_set_context(source, @"某个数据指针,记得在取消处理器中释放");
    
    dispatch_source_set_event_handler(source, ^{
        // Get some data from the source variable, which is captured
        // from the parent context.
        size_t estimated = dispatch_source_get_data(source);//获取源数据
        NSLog(@"设置事件处理器:处理data-%zu",estimated)
        ;
        // Continue reading the descriptor...
    });
    
    //    dispatch_source_set_event_handler_f(source,  _dispatch_callback);//??
    
    dispatch_source_merge_data(source, 100);//合并源数据
    dispatch_resume(source);    //开始处理事件
}

     5.修改目标Queue:

     在创建 dispatch source 时可以指定一个 queue,用来执行事件处理器和取消 处理器。不过你也可以使用dispatch_set_target_queue 函数在任何时候修改目标 queue。修改 queue 可以改变执行 dispatch source 事件的优先级。

     

     修改 dispatch source 的目标 queue 是异步操作,dispatch source 会尽可能快 地完成这个修改。如果事件处理器已经进入 queue 并等待处理,它会继续在原来 的 Queue 中执行。随后到达的所有事件的处理器都会在后面修改的 queue 中执 行。

     

     6.关联自定义数据到dispatch source:

     和 GCD 的其它类型一样,你可以使用 dispatch_set_context 函数关联自定义 数据到 dispatch source。使用 context 指针存储事件处理器需要的任何数据。如 果你在 context 指针中存储了数据,你就应该安装一个取消处理器,在 dispatch source 不再需要时释放这些 context 自定义数据。

     

     如果你使用 block 实现事件处理器,你也可以捕获本地变量,并在 Block 中使 用。虽然这样也可以代替context指针,但是你应该明智地使用Block捕获变量。 因为 dispatch source 长时间存在于应用中,Block 捕获指针变量时必须非常小心, 因为指针指向的数据可能会被释放,因此需要复制数据或 retain。不管使用哪种 方法,你都应该 供一个取消处理器,在最后释放这些数据。

     

     7.dispatch source的内存管理:

     dispatch source 的所有权可以由 dispatch source 内部或外部进行管理。外部 所有权时,另一个对象拥有 dispatch source,并负责在不需要时释放它。内部所 有权时,dispatch source 自己拥有自己,并负责在适当的时候释放自己。虽然外 部所有权很常用,当你希望创建自主 dispatch source,并让它自己管理自己的行 为时,可以使用内部所有权。例如 dispatch source 应用单一全局事件时,可以让 它自己处理该事件,并立即退出。

     

     8.Dispatch Source示例:

     1)创建一个定时器:

     定时器 dispatch source 定时产生事件,可以用来发起定时执行的任务,如游 戏或其它图形应用,可以使用定时器来更新屏幕或动画。你也可以设置定时器, 并在固定间隔事件中检查服务器的新信息。

     所有定时器 dispatch source 都是间隔定时器,一旦创建,会按你指定的间隔 定期递送事件。你需要为定时器 dispatch source 指定一个期望的定时器事件精度, 也就是 leeway 值,让系统能够灵活地管理电源并唤醒内核。例如系统可以使用 leeway 值来 前或延迟触发定时器,使其更好地与其它系统事件结合。创建自 己的定时器时,你应该尽量指定一个 leeway 值。

     就算你指定 leeway 值为 0,也不要期望定时器能够按照精确的纳秒来触发事 件。系统会尽可能地满足你的需求,但是无法保证完全精确的触发时间。

     当计算机睡眠时,定时器 dispatch source 会被挂起,稍后系统唤醒时,定时 器 dispatch source 也会自动唤醒。如果定时器 dispatch source 使用 dispatch_time 函数 或 DISPATCH_TIME_NOW 常量设置,定时器 dispatch source 会使用系统默认时钟 来确定何时触发,但是默认时钟在计算机睡眠时不会继续。

     

     如果你使用 dispatch_walltime 函数来设置定时器 dispatch source,则定时器 会根据挂钟时间来跟踪,这种定时器比较适合触发间隔相对比较大的场合,可以 防止定时器触发间隔出现太大的误差。

     

     演示-:

     演示一个定时器dispatch source的一个例子,每30秒触发一次,leeway值为1,因为时间间隔相对较大,使用dispatch_walltime来创建定时器;

     定时器会立即触发第一次,随后每30秒触发一次;

     MyPeriodicTask和MyStoreTimer是自定义函数,用于实现定时器的行为,并存储定时器到应用的数据结构;

     下面一段时间倒计时的实现,同作为演示的一部分,倒计时结束之后可以用dispatch_source_cancel把定时器dispatch source取消掉;;

     

     虽然定时器dispatch source是接收时间事件的主要方法,你还可以使用其他选择;如果想在指定时间间隔后执行block,可以使用dispatch_after 或 dispatch_after_f 函数;这两个函数非常类似于dispatch_async,但是只允许你指定一个时间值,时间一到就自动提交block到queue中执行,时间值可以指定为相对或绝对时间。

     

/**
 * 四:8-1)
 */
dispatch_source_t CreateDispatchTimer(uint64_t interval,    //间距
                                      uint64_t leeway,      //精度
                                      dispatch_queue_t queue,
                                      dispatch_block_t block){
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    if (timer) {
        dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
        dispatch_source_set_event_handler(timer, block);
        dispatch_resume(timer);
    }
    return timer;
}
void MyPeriodicTask(){
    printf("30s after,do something.\n");
}
void MyStroeTimer(dispatch_source_t timer){
    printf("store timer for use.\n");
}
void MyCreateTimer(){
    
    /*
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     dispatch_source_t aTimer = CreateDispatchTimer(30.0 * NSEC_PER_SEC, 1 , queue, ^{
     MyPeriodicTask();
     });
     //store it somewhere for later use
     
     if (aTimer) {
     MyStroeTimer(aTimer);
     }
     */
    
    /*
     __block int timeout=30; //倒计时时间
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
     
     dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行
     dispatch_source_set_event_handler(_timer, ^{
     if(timeout<=0){ //倒计时结束,关闭
     dispatch_source_cancel(_timer);
     dispatch_async(dispatch_get_main_queue(), ^{
     //设置界面的按钮显示 根据自己需求设置
     //[l_timeButton setTitle:@"发送验证码" forState:UIControlStateNormal];
     //l_timeButton.userInteractionEnabled = YES;
     });
     }else{
     //            int minutes = timeout / 60;
     int seconds = timeout % 60;
     NSString *strTime = [NSString stringWithFormat:@"%.2d", seconds];
     dispatch_async(dispatch_get_main_queue(), ^{
     //设置界面的按钮显示 根据自己需求设置
     NSLog(@"____%@",strTime);
     //[l_timeButton setTitle:[NSString stringWithFormat:@"%@秒后重新发送",strTime] forState:UIControlStateNormal];
     //l_timeButton.userInteractionEnabled = NO;
     
     });
     timeout--;
     
     }
     });
     dispatch_resume(_timer);
     */
    
    
    //创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("com.test.gcd", DISPATCH_QUEUE_CONCURRENT);
    //立即打印一条信息
    NSLog(@"Begin add block...");
    //提交一个block
    dispatch_async(queue, ^{
        //Sleep 10秒
        [NSThread sleepForTimeInterval:10];
        NSLog(@"First block done...");
    });
    //5 秒以后提交block
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
        NSLog(@"After...");
    });
    
}

     2)从描述附中读取数据:

     要从文件或 socket 中读取数据,需要打开文件或 socket,并创建一个 DISPATCH_SOURCE_TYPE_READ 类型的 dispatch source。你指定的事件处理器 必须能够读取和处理 述符中的内容。对于文件,需要读取文件数据,并为应用 创建适当的数据结构;对于网络 socket,需要处理最新接收到的网络数据。

     

     读取数据时,你总是应该配置 述符使用非阻塞操作,虽然你可以使用 dispatch_source_get_data 函数查看当前有多少数据可读,但在你调用它和实 际读取数据之间,可用的数据数量可能会发生变化。如果底层文件被截断,或发 生网络错误,从 述符中读取会阻塞当前线程,停止在事件处理器中间并阻止 dispatch queue 去执行其它任务。对于串行 queue,这样还可能会死锁,即使是 并发 queue,也会减少 queue 能够执行的任务数量。

     

     演示-:

     下面例子配置dispatch source从文件中读取数据,事件处理器读取指定文件的全部内容到缓冲区,并调用一个自定义函数来处理这些数据;

     调用方可以使用返回的dispatch source在读取操作完成之后,来取消这个事件;

     为了确保dispatch source不会阻塞,这里使用了fcntl函数,配置文件描述符执行非阻塞操作;

     dispatch source安装了取消处理器,确保最后关闭了文件描述符;

     

/**
 * 四:8-2)
 */
Boolean MyProcessFileData(char * buffer,ssize_t actual){
    
    printf("%c\n",*buffer);
    printf("%zd\n",actual);
    printf("%s\n",buffer);//buffer 即为当前文件中内容(示例是一段文本)
    
    return YES;
}
dispatch_source_t ProcessContentsOfFile(const char * filename){
    //Prepare the file for reading
    
    int fd = open(filename,O_RDONLY);
    if (fd == -1) {
        return NULL;
    }
    fcntl(fd, F_SETFL,O_NONBLOCK);//
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue);
    
    if (!readSource) {
        close(fd);
        return NULL;
    }
    
    dispatch_source_set_event_handler(readSource, ^{
        
        size_t estimated = dispatch_source_get_data(readSource) + 1;
        //read the data into a text buffer
        char * buffer = (char *)malloc(estimated);
        if (buffer) {
            ssize_t actual = read(fd, buffer, (estimated));
            Boolean done = MyProcessFileData(buffer,actual);//Process the data
            
            free(buffer);
            
            if (done) {
                dispatch_source_cancel(readSource);
            }
        }
        
    });
    dispatch_source_set_cancel_handler(readSource, ^{
        close(fd);
    });
    
    dispatch_resume(readSource);
    
    return readSource;
}

     3)向描述符写入数据:

     向文件或 socket 写入数据非常类似于读取数据,配置 述符为写入操作后, 创建一个 DISPATCH_SOURCE_TYPE_WRITE 类型的 dispatch source,创建好之后, 系统会调用事件处理器,让它开始向文件或 socket 写入数据。当你完成写入后, 使用 dispatch_source_cancel 函数取消 dispatch source。

     

     写入数据也应该配置文件 述符使用非阻塞操作,虽然 dispatch_source_get_data 函数可以查看当前有多少可用写入空间,但这个值只 是建议性的,而且在你执行写入操作时可能会发生变化。如果发生错误,写入数 据到阻塞 述符,也会使事件处理器停止在执行中途,并阻止 dispatch queue 执 行其它任务。串行 queue 会产生死锁,并发 queue 则会减少能够执行的任务数 量。

     

     演示-:

     下面是使用 dispatch source 写入数据到文件的例子,创建文件后,函数传递 文件 述符到事件处理器。MyGetData 函数负责提供要写入的数据,在数据写入 到文件之后,事件处理器取消 dispatch source,阻止再次调用。此时 dispatch source 的拥有者需负责释放 dispatch source。

     对C的文件操作函数不是太熟悉,最终写成功,仅作示例使用;

     

/**
 * 四:8-3)
 */

size_t MyGetData(void * buffer,size_t bufferSize){
    return bufferSize;
}
dispatch_source_t WriteDataToFile(const char * filename){
    int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC,(S_IRUSR|S_IWUSR|S_ISUID|S_ISGID));
    
    if (fd == -1) {
        return NULL;
    }
    fcntl(fd, F_SETFL);//block during the write
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, queue);
    
    if (!writeSource) {
        close(fd);
        return NULL;
    }
    
    dispatch_source_set_event_handler(writeSource, ^{
        size_t bufferSize = 100;
        char * buffer = (char *)malloc(bufferSize + 1);
        buffer = strcpy(buffer, "flower flower flower flower flower flower flower\n");
        size_t actual = MyGetData(buffer,bufferSize);
        write(fd, buffer, actual);
        
        free(buffer);
        
        dispatch_source_cancel(writeSource);
    });
    
    dispatch_source_set_cancel_handler(writeSource, ^{
        close(fd);
    });
    dispatch_resume(writeSource);
    
    return writeSource;
    
}

     4)监控文件系统对象:

     如果需要监控文件系统对象的变化,可以设置一个DISPATCH_SOURCE_TYPE_VNODE类型的dispatch source,你可以从这个dispatch source中接收文件删除、写入、重命名等通知;你还可以得到文件的特定元数据信息变化通知;

     在dispatch source正在处理事件时,dispatch source中指定的文件描述符必须保持打开状态;

     

     演示-:

     下面例子监控一个文件的文件名变化,并在文件名变化时执行一些操作(自定义的MyUpdateFileName函数)。由于文件描述符专门为dispatch source打开,dispatch source安装了取消处理器来关闭文件描述符;这个例子中的文件描述符关联到底层的文件系统对象,因此同一个dispatch source可以用来检测多次文件名变化;

     

/**
 * 四:8-4)
 */
void MyUpdataFilename(const char * oldFilename , int fd){
    
}
dispatch_source_t MonitorNameChangesToFile(const char * filename){
    int fd = open(filename , O_EVTONLY);
    
    if (fd == -1) {
        return NULL;
    }
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_RENAME, queue);
    
    if (source) {
        //copy the filename for later use
        int length = (int)strlen(filename);
        char * newString = (char *)malloc(length + 1);
        newString = strcpy(newString, filename);
        dispatch_set_context(source, newString);
        
        //Install the event handler to process the name change
        dispatch_source_set_event_handler(source, ^{
            const char * oldFilename = (char *)dispatch_get_context(source);
            MyUpdataFilename(oldFilename,fd);
        });
        
        //Install a cancellation handler to free the descriptor
        //and the stored string
        
        dispatch_source_set_cancel_handler(source, ^{
            char * fileStr = (char *)dispatch_get_context(source);
            free(fileStr);
            close(fd);
        });
        
        //start processing events
        dispatch_resume(source);
    }else{
        close(fd);
    }
    
    return source;
}

     5)监测信号:

     应用可以接收许多不同类型的信号,如不可恢复的错误(非法指令)、或重 要信息的通知(如子进程退出)。传统编程中,应用使用 sigaction 函数安装信 号处理器函数,信号到达时同步处理信号。如果你只是想信号到达时得到通知, 并不想实际地处理该信号,可以使用信号 dispatch source 来异步处理信号。

     

     信号 dispatch source 不能替代 sigaction 函数 供的同步信号处理机制。同步 信号处理器可以捕获一个信号,并阻止它中止应用。而信号 dispatch source 只允 许你监测信号的到达。此外,你不能使用信号 dispatch source 获取所有类型的信 号,如 SIGILL, SIGBUS, SIGSEGV 信号。

     

     由于信号 dispatch source 在 dispatch queue 中异步执行,它没有同步信号处 理器的一些限制。例如信号 dispatch source 的事件处理器可以调用任何函数。灵 活性增大的代价是,信号到达和 dispatch source 事件处理器被调用的延迟可能会 增大。

     

     演示-:

     下面例子配置信号dispatch source来处理SIGHUP信号,事件处理器用MyProcessSIGHUP函数,用来处理信号;

     

/**
 * 四:8-5)
 */
void MyProcessSIGHUP(){
    printf("监测到了SIGHUP信号");
}

void InstallSignalHandler(){
    //make sure the signal does not terminate the application
    signal(SIGHUP, SIG_IGN);

    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGHUP, 0, queue);
    
    if (source) {
        dispatch_source_set_event_handler(source, ^{
            MyProcessSIGHUP();
        });
    
        //start processing signals
        dispatch_resume(source);
    }
}

     这个例子没太看懂,貌似相应类别的信号没有触发,试了下干掉进程 SIGKILL对应的信号,系统log未打印出来,应该是收到了;

     

     6)监控进程:

     进程 dispatch source 可以监控特定进程的行为,并适当地响应。父进程可以 使用 dispatch source 来监控自己创建的所有子进程,例如监控子进程的死亡;类 似地,子进程也可以使用 dispatch source 来监控父进程,例如在父进程退出时自 己也退出。

     

     演示-:

     下面例子安装了一个进程 dispatch source,监控父进程的终止。当父进程退 出时,dispatch source 设置一些内部状态信息,告知子进程自己应该退出。 MySetAppExitFlag 函数应该设置一个适当的标志,允许子进程终止。由于 dispatch source 自主运行,因此自己拥有自己,在程序关闭时会取消并释放自己。

     代码如下,未做测试;

     

/**
 * 四:8-6)
 */
void MySetAppExitFlag(){
    printf("父进程退出 告知子进程终止");
}
void MonitorParentProcess(){
    pid_t parentPID = getppid();
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, parentPID, DISPATCH_PROC_EXEC, queue);
    
    if (source) {
        dispatch_source_set_event_handler(source, ^{
            MySetAppExitFlag();
            dispatch_source_cancel(source);
        });
        
        //start processing signals
        dispatch_resume(source);
    }
}

     9.取消一个dispatch source:

     除非你现实地调用dispatch_source_cancel函数,dispatch source将一直保持活动,取消一个dispatch source会停止递送新事件,并且不能撤销;

     因此你通常在取消之后释放他;

     void RemoveDispatchSource(dispatch_source_t mySource) {

     dispatch_source_cancel(mySource);

     dispatch_release(mySource); 

     }

     取消一个 dispatch source 是异步操作,调用 dispatch_source_cancel 之后,不 会再有新的事件被处理,但是正在被 dispatch source 处理的事件会继续被处理完 成。在处理完最后的事件之后,dispatch source 会执行自己的取消处理器。

     取消处理器是你最后的执行机会,在那里执行内存或资源的释放工作。例如  述符或 mach port 类型的 dispatch source,必须 供取消处理器,用来关闭  述符或 mach port

     

     10.挂起和继续Dispatch source:

     你可以使用 dispatch_suspend 和 dispatch_resume 临时地挂起和继续 dispatch source 的事件递送。这两个函数分别增加和减少 dispatch 对象的挂起计 数。因此,你必须每次 dispatch_suspend 调用之后,都需要相应的 dispatch_resume 才能继续事件递送。

     

     挂起一个 dispatch source 期间,发生的任何事件都会被累积,直到 dispatch source 继续。但是不会递送所有事件,而是先合并到单一事件,然后再一次递送。 例如你监控一个文件的文件名变化,就只会递送最后一次的变化事件。



总结:


     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值