Dispatch Sources

1、简介网络

Dispatch Sources经常使用于处理跟系统有关的事件,协调处理指定的低级别的系统事件。在配置Dispatch Source时,需指定监控的事件类型、Dispatch Queues、Event Handle(blocks/functions)。当被监控的事件发生时,Dispatch Source提交Event Handle到指定的Dispatch Queues。app

不一样于手动提交到queue中的任务,dispatch sources给应用提供了持续的事件资源。dispatch source除了明确取消,不然会持续与dispatch queue相关联。无论何时指定的事件发生时,就会提交任务到关联着的dispatch queue中。例如,定时器事件周期性的发生,还有大多数只有在指定条件下才发生的事件。为此,dispatch sources持有关联的dispatch queue,避免事件仍然会发生而dispatch queue被释放了。异步

为了不event handle被积压在某个dispatch queue中,dispatch sources实现事件合并方案。若是前一个任务已出列并在处理时,新的事件到来了,dispatch source合并新事件和旧事件的数据。合并规则取决于事件的类型,合并可能代替旧事件,或者更新旧事件的数据。例如,基于信号的dispatch source会提供最近相关的信息,但也报告自从上次事件处理发生以来总共发出了多少信号量。socket

Dispatch Sources包括这几类:Timer dispatch sources、Signal dispatch sources、Descriptor sources、Process dispatch sources、Mach port dispatch sources和Custom dispatch sources。async

一、Timer dispatch sources周期性通知。
二、Signal dispatch sources为unix信号发出时通知。
三、Descriptor sources各类各样的file-和socket-操做通知。如从文件或者网络中读/写数据,或文件名被重命名,或文件被删、被移动、数据内容改动时。
四、Process dispatch sources父子process退出时等等操做通知。
五、Mach port dispatch sources
六、Custom dispatch sources函数

 

 

2、建立Dispatch Sources动画

dispatch_source_create函数返回的是出于暂停状态的dispatch source,在暂停状态时,dispatch source接收通知但并不执行event handle。ui

一、Event Handlespa

event handle用于处理dispatch source的通知,经过dispatch_source_set_event_handle函数,为dispatch source建立function/block类型的event handle。当事件到来时,dispatch source提交event handle到指定的dispatch queue。unix

event handle为处理即将到来的全部事件负责。假设上一个event handle已经在队列中等待被执行,又有新的event handle请求添加到queue中,dispatch source会合并两个事件。然而当event handel正在执行,dispatch source等待它执行结束后,再将event handle提交到queue中。

// 基于block的event handle没有参数也没有返回值。
void (^dispatch_block_t)(void)
 
// 基于function的event handle包括上下文指针和dispatch source对象,无返回值。
void (*dispatch_function_t)(void *)
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
                                 myDescriptor, 0, myQueue);
dispatch_source_set_event_handler(source, ^{
   // block从外部捕获到source变量
   size_t estimated = dispatch_source_get_data(source);
 
   // Continue reading the descriptor...
});
dispatch_resume(source);

二、Cancellation Handle

Cancellation handle用于在dispatch source被释放前清理dispatch source。在大多数dispatch source类型中它是选择性被实现,除了descriptor/mach port dispatch source需经过cancellation handle去关闭descriptor和释放mach port。

dispatch_source_set_cancel_handler(mySource, ^{
   close(fd); // Close a file descriptor opened earlier.
});

三、Target Queue

在建立dispatch source须要指定调度event handle/cancellation handle的dispatch queue。在指定以后,还能够经过dispatch_set_target_queue函数修改关联的dispatch queue。通常修改target queue是用于修改queue的优先级,该操做是异步操做。所以在作修改操做前,已在旧dispatch queue中的任务继续被调度执行。若是刚好在修改过程当中,添加任务到queue,该queue多是旧queue,也多是新queue。

四、Custom Data

跟GCD同样,dispatch source能够经过dispatch_set_context关联自定义数据,原理是经过context pointer存储event handle中须要用到的数据。注意的是建立了context pointer,就必须经过cancellation handle最终释放那些存储的数据。

另外一种方案是经过event handle用block实现,虽然也能捕获变量,但变量随时可能被释放。所以这种方案须要经过拷贝并持有数据防止变量被回收,最终再经过cancellation handle释放该变量。

五、Memory Management

知足内存管理原则,能够经过dispatch_retain/dispatch_release来控制。

 

3、Dispatch Source案例

一、Create a Timer

timer dispatch source是周期性的timers,类型为DISPATCH_SOURCE_TYPE_TIMER,leeway值是设置的容差值,若是leeway为0,系统也没法保证在指定周期执行任务。它经常使用于游戏等应用刷新频幕和动画。

当电脑进入休眠时,timer dispatch source也被暂停,电脑恢复时它恢复,暂停会影响timer下一次执行。若是经过dispatch_time建立的timer,时间为相对时间,它会使用系统闹钟,系统闹钟在电脑休眠时不会转动。但若是经过 dispatch_walltime建立的timer,时间为绝对时间,它使用wall闹钟,经常使用于大的时间间隔。

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_walltime()函数
      dispatch_source_set_event_handler(timer, block);
      dispatch_resume(timer);
   }
   return timer;
}
 
void MyCreateTimer()
{
   // 每30秒执行一次,容差1秒,event handle中具体实现为MyPeriodicTask()
   dispatch_source_t aTimer = CreateDispatchTimer(30ull * NSEC_PER_SEC, 1ull * NSEC_PER_SEC, dispatch_get_main_queue(), ^{ MyPeriodicTask(); });
 
   // Store it somewhere for later use.
    if (aTimer){
        MyStoreTimer(aTimer);
    }
}

 除了timer dispatch source按期处理系统事件,还有dispatch_after在指定时间以后执行一次某事件,dispatch_after就像指定了时间的dispatch_async函数。

二、Reading Data from a Descriptor 

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;
   }
 
   // Event Handler
   dispatch_source_set_event_handler(readSource, ^{
    size_t estimated
= dispatch_source_get_data(readSource) + 1; // 读取数据至buffer char* buffer = (char*)malloc(estimated); if (buffer){ ssize_t actual = read(fd, buffer, (estimated)); Boolean done = MyProcessFileData(buffer, actual); // 处理数据 free(buffer); // 读取完毕,取消该source。 if (done) dispatch_source_cancel(readSource); } }); // Cancellation Handler dispatch_source_set_cancel_handler(readSource, ^{close(fd);}); // 开始读文件 dispatch_resume(readSource);
return readSource; }  

三、Writing Data to a Descriptor

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 = MyGetDataSize();
        void* buffer = malloc(bufferSize);
 
        size_t actual = MyGetData(buffer, bufferSize);
        write(fd, buffer, actual);
 
        free(buffer);
 
        // Cancel and release the dispatch source when done.
        dispatch_source_cancel(writeSource);
    });
 
    dispatch_source_set_cancel_handler(writeSource, ^{close(fd);});
    dispatch_resume(writeSource);
    return (writeSource);
}

四、Monitoring a File-System Object

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 = 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);
            MyUpdateFileName(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;
}

五、Monitoring Signals

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

六、Monitoring a Process

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_EXIT, queue);
   if (source){
      dispatch_source_set_event_handler(source, ^{
         MySetAppExitFlag();
         dispatch_source_cancel(source);
         dispatch_release(source);
      });
      dispatch_resume(source);
   }
}

 

4、取消Dispatch Source

void RemoveDispatchSource(dispatch_source_t mySource)
{
   dispatch_source_cancel(mySource);
   dispatch_release(mySource);
}

 

5、暂停和恢复Dispatch Source

相关文章
相关标签/搜索