GCD里就有三种queue(分派队列)来处理.

1. Main queue:(主队列)

  顾名思义,运行在主线程,由dispatch_get_main_queue获得.和ui相关的就要使用Main Queue.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 耗时的操作

dispatch_async(dispatch_get_main_queue(), ^{

// 更新界面

});

});

2.Serial quque(private dispatch queue,其中dispatch_queue_t就是一种)  (串行队列)

 每次运行一个任务,可以添加多个,执行次序FIFO. 通常是指程序员生成的,比如:

dispatch_async(queueName, block1);

dispatch_async(queueName, block2);

block1和block2在同一个queue下执行, 但会先执行block1, 待block1执行完毕后, 才会执行block2.

3. Concurrent queue(global dispatch queue,其中dispatch_time_t就是一种): (并发队列)

可以同时运行多个任务,每个任务的启动时间是按照加入queue的顺序,结束的顺序依赖各自的任务.使用dispatch_get_global_queue获得.比如:

dispatch_async(dispatch_get_global_queue(0,0), block1);

dispatch_async(dispatch_get_global_queue(0,0), block2);

两个block会同时运行.

基本使用命令:

1.创建一个queue:

dispatch_queue_t queueName= dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

这里需要注意label是C语言的字符串形式,不是OC的NSString.

const char *queueName = [daStr UTF8String]; 转换方式

dispatch_queue_t queueName= dispatch_queue_create("queueName", dispatch_queue_attr_t attr); //不是@"queueName"

dispatch_queue_attr_t attr  写成NULL即可.

在非ARC的情况下, create的定义, 需要release

dispatch_release(queueName);

2. 暂停一个队列

如果需要暂停一个队列,可以调用如下代码。暂停一个队列会阻止和该队列相关的所有代码运行。

dispatch_suspend(myQueue);

3.恢复一个队列

如果暂停一个队列不要忘记恢复。暂停和恢复的操作和内存管理中的retain和release类似。调用dispatch_suspend会增加暂停计数,而dispatch_resume则会减少。队列只有在暂停计数变成零的情况下才开始运行。

dispatch_resume(myQueue);

针对Serial quque, 无论暂停或者恢复队列, 都是以block为单位, 正在执行的block不会因为suspend而停止, suspend只会影响下一个block的执行.

4.从队列中在主线程运行代码

有些操作无法在异步队列运行,因此必须在主线程(每个应用都有一个)上运行。UI绘图以及任何对NSNotificationCenter的调用必须在主线程长进行。

在另一个队列中访问主线程并运行代码的示例如下:

dispatch_sync(dispatch_get_main_queue(), ^{ [self dismissLoginWindow]; });注意,dispatch_suspend (以及dispatch_resume)在主线程上不起作用。

5.创建一个semaphore

semaphore的主要用途在一个进程中通知另一个进程开始运行

dispatch_semaphore_ t sem=dispatch_semaphore_create(0);  //定一个semaphore, 设置计数为0

diapatch_async(queueName,^(){

for(int i=0;i<10;i++){

if (i==5) dispatch_semaphore_sinal(sem);  //sem技术器加1

}};

dispatch_semaphore_waite(sem,DISPATH_TIME_FOREVER);   //sem技术器减1, 只有sem<0, 表明资源不可得,不能运行.

nslog(@"run");   //当i=5的时候, run开始运行.

可以用于线程阻塞.

6.dispatch_group_async的使用

dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。下面是一段例子代码:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{

[NSThread sleepForTimeInterval:1];

NSLog(@"group1");

});

dispatch_group_async(group, queue, ^{

[NSThread sleepForTimeInterval:2];

NSLog(@"group2");

});

dispatch_group_async(group, queue, ^{

[NSThread sleepForTimeInterval:3];

NSLog(@"group3");

});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

NSLog(@"updateUi");

});

dispatch_release(group);

dispatch_group_async是异步的方法,运行后可以看到打印结果:

2012-09-25 16:04:16.737 gcdTest[43328:11303] group1

2012-09-25 16:04:17.738 gcdTest[43328:12a1b] group2

2012-09-25 16:04:18.738 gcdTest[43328:13003] group3

2012-09-25 16:04:18.739 gcdTest[43328:f803] updateUi

每个一秒打印一个,当第三个任务执行后,upadteUi被打印。

7.dispatch_barrier_async的使用

dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行

例子代码如下:

dispatch_queue_t queue = dispatch_queue_create("gcdtest.rongfzh.yc", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{

[NSThread sleepForTimeInterval:2];

NSLog(@"dispatch_async1");

});

dispatch_async(queue, ^{

[NSThread sleepForTimeInterval:4];

NSLog(@"dispatch_async2");

});

dispatch_barrier_async(queue, ^{

NSLog(@"dispatch_barrier_async");

[NSThread sleepForTimeInterval:4];

});

dispatch_async(queue, ^{

[NSThread sleepForTimeInterval:1];

NSLog(@"dispatch_async3");

});

8、dispatch_apply

 

并发执行循环迭代
使用dispatch_apply或dispatch_apply_f函数。 如果你要使用这两个函数执行类似

for (int i = 0; i < 10; i++) {
2 printf("loop-----%i\n", i);
3 }

的任务,可以使用

1 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2 dispatch_apply(10, queue, ^(size_t i) {
3 printf("loop-----%zd\n", i);
4 });

1 void loopwork(void *p, size_t i) {
2 printf("loop-----%zd\n", i);
3 }
4
5 - (void)viewDidLoad {
6 [super viewDidLoad];
7 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
8 dispatch_apply_f(10, queue, NULL, loopwork);
9 }

9.Dispatch After

主要用于延迟执行一些代码。

例子:

int64_t delayInSeconds = 1.0;

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

[MBProgressHUD hideHUDForView:self.view animated:YES];

[self dismissModalViewControllerAnimated:YES];

});

⒁? NSEC_PER_SEC 的使用

说明:首先声明一个时间,之后从现在开始计时,一旦过了特定的时间后就执行after代码块中的内容。

10.Dispatch Once

只执行一次,用于一些单例。

例子:

static SKTraktAPIClient *_sharedClient = nil;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

_sharedClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:kTraktBaseURLString]];

});

return _sharedClient;

11. Dispatch Sources

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

dispatch source是基础数据类型,协调特定底层系统事件的处理。Grand Central Dispatch支持以下dispatch source:

Timer dispatch source:定期产生通知

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

Descriptor dispatch source:各种文件和socket操作的通知

数据可读

数据可写

文件在文件系统中被删除、移动、重命名

文件元数据信息改变

Process dispatch source:进程相关的事件通知

当进程退出时

当进程发起fork或exec等调用

信号被递送到进程

Mach port dispatch source:Mach相关事件的通知

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会将新旧事件的数据合并。根据事件类型的不同,合并操作可能会替换旧事件,或者更新旧事件的信息。

创建Dispatch Source

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

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

使用 dispatch_source_create 函数创建dispatch source

配置dispatch source:

为dispatch source设置一个事件处理器

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

为dispatch source赋予一个取消处理器(可选)调用 dispatch_resume 函数开始处理事件由于dispatch source必须进行额外的配置才能被使用,dispatch_source_create 函数返回的dispatch source将处于挂起状态。此时dispatch source会接收事件,但是不会进行处理。这时候你可以安装事件处理器,并执行额外的配置。

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

你需要定义一个事件处理器来处理事件,可以是函数或block对象,并使用 dispatch_source_set_event_handler 或 dispatch_source_set_event_handler_f 安装事件处理器。事件到达时,dispatch source会提交你的事件处理器到指定的dispatch queue,由queue执行事件处理器。

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

如果事件处理器已经开始执行,一个或多个新事件到达,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定义时会自动捕获到外部定义的所有变量。

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,

myDescriptor, 0, myQueue);

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

// Continue reading the descriptor...

});

dispatch_resume(source);

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

下面是事件处理器能够获得的事件信息:

函数描述

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,函数返回的值未定义

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

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

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

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

对于进程dispatch source,函数返回一个常量,表示发生的事件类型,参考 dispatch_source_proc_flags_t 枚举类型

对于Mach port dispatch source,函数返回一个常量,表示发生的事件类型,参考 dispatch_source_machport_flags_t 枚举类型

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

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,函数返回用来合并数据值的掩码。

安装一个取消处理器

取消处理器在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 函数来设置取消处理器。

下面取消处理器关闭描述符:

dispatch_source_set_cancel_handler(mySource, ^{

close(fd); // Close a file descriptor opened earlier.

});

修改目标Queue

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

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

关联自定义数据到dispatch source

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

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

Dispatch Source的内存管理

Dispatch Source也是引用计数的数据类型,初始计数为1,可以使用 dispatch_retain 和 dispatch_release 函数来增加和减少引用计数。引用计数到达0时,系统自动释放dispatch source数据结构。

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

Dispatch Source示例

创建一个定时器

定时器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_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 MyCreateTimer()

{

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

}

}

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

从描述符中读取数据

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

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

下面例子配置dispatch source从文件中读取数据,事件处理器读取指定文件的全部内容到缓冲区,并调用一个自定义函数来处理这些数据。调用方可以使用返回的dispatch source在读取操作完成之后,来取消这个事件。为了确保dispatch queue不会阻塞,这里使用了fcntl函数,配置文件描述符执行非阻塞操作。dispatch source安装了取消处理器,确保最后关闭了文件描述符。

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);  // Avoid blocking the read operation

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;

}

// Install the event handler

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.

// Release the buffer when done.

free(buffer);

// If there is no more data, cancel the source.

if (done)

dispatch_source_cancel(readSource);

}

});

// Install the cancellation handler

dispatch_source_set_cancel_handler(readSource, ^{close(fd);});

// Start reading the file.

dispatch_resume(readSource);

return readSource;

}

在这个例子中,自定义的 MyProcessFileData 函数确定读取到足够的数据,返回YES告诉dispatch source读取已经完成,可以取消任务。通常读取描述符的dispatch source在还有数据可读时,会重复调度事件处理器。如果socket连接关闭或到达文件末尾,dispatch source自动停止调度事件处理器。如果你自己确定不再需要dispatch source,也可以手动取消它。

向描述符写入数据

向文件或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。

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

}

监控文件系统对象

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

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

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

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;

}

监测信号

应用可以接收许多不同类型的信号,如不可恢复的错误(非法指令)、或重要信息的通知(如子进程退出)。传统编程中,应用使用 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 函数,用来处理信号。

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

}

}

监控进程

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

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

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

}

}

取消一个Dispatch Source

除非你显式地调用 dispatch_source_cancel 函数,dispatch source将一直保持活动,取消一个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

挂起和继续Dispatch Source

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

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

一个 GCD 倒计时的例子:

         __block    NSInteger timeout = ;

         dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, , ,queue);
dispatch_source_set_timer(_timer,dispatch_walltime(NULL, ),1.0*NSEC_PER_SEC, ); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
if(timeout<=){ //倒计时结束,关闭
dispatch_source_cancel(_timer);
dispatch_async(dispatch_get_main_queue(), ^{
//设置界面的按钮显示 根据自己需求设置 });
}else{
dispatch_async(dispatch_get_main_queue(), ^{
//设置界面的按钮显示 根据自己需求设置 });
timeout--;
}
});
dispatch_resume(_timer);

iOS GCD 拾遗的更多相关文章

  1. iOS GCD基础篇 - 同步、异步,并发、并行的理解

    1.关于GCD - GCD全称是Grand Central Dispatch  - GCD是苹果公司为多核的并行运算提出的解决方案  - GCD会自动利用更多的CPU内核(比如双核.四核)  - GC ...

  2. iOS GCD之dispatch_semaphore(信号量)

    前言 最近在看AFNetworking3.0源码时,注意到在 AFURLSessionManager.m 里面的 tasksForKeyPath: 方法 (L681),dispatch_semapho ...

  3. iOS GCD 编程小结

    一.简单介绍 1.GCD简介? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD优势 GCD是苹果公司为多核的并行运算提出的 ...

  4. iOS GCD NSOperation NSThread等多线程各种举例详解(拷贝)

    2年多的iOS之路匆匆而过,期间也拜读来不少大神的博客,近来突然为自己一直做伸手党感到羞耻,是时候回馈社会.回想当年自己还是小白的时候,照着一些iOS多线程教程学,也只是照抄,只知其然.不知其所以然. ...

  5. ios - GCD简单小结

    首先GCD两个名词: 队列 同步异步. 队列: 任务放到队列,队列中的任务执行方式取决于执行队列中任务的方式---同步异步. 串行队列: 任务顺序执行,可以叫阻塞队列.只有前面任务完成才执行后面的. ...

  6. iOS GCD 与 NSOperationQueue

    NSOperationQueue ios NSOperation vs. GCD StackOverflow: NSOperation vs. Grand Central Dispatch Blog: ...

  7. IOS GCD 使用 (二)

     上一节,主要介绍了GCD的基本的概念,这节将用代码深入详细介绍GCD的使用. 一  使用介绍    GCD的使用主要分为三步:创建代码块;选择或创建合适的分发队列;(同步.异步方式)向分发队列提交任 ...

  8. iOS——GCD多线程

    1> 概述 Grand Central Dispatch (GCD)是Apple开发的一种多核编程技术.主要用于优化应用程序以支持多核处理器以及其他对称多处理系统. GCD提供函数实现多线程开发 ...

  9. IOS GCD 的理解

    GCD (Grand Central Dispatch) 是Apple公司开发的一种技术,它旨在优化多核环境中的并发操作并取代传统多线程的编程模式. 在Mac OS X 10.6和IOS 4.0之后开 ...

随机推荐

  1. hadoop2.5.2学习及实践笔记(四)—— namenode启动过程源码概览

    对namenode启动时的相关操作及相关类有一个大体了解,后续深入研究时,再对本文进行补充 >实现类 HDFS启动脚本为$HADOOP_HOME/sbin/start-dfs.sh,查看star ...

  2. 在Eclipse中调用weka包实现分类

    1.如题. 最近写了一个FCM的聚类算法,希望能够可视化结果,因此一个想法是调用weka中的包,使自己的程序可以可视化.这里参考了网络上的方法,首先实现在Eclipse中调用weka包实现分类的功能. ...

  3. bzoj1494【Noi2007】生成树计数

    题意:http://www.lydsy.com/JudgeOnline/problem.php?id=1494 sol  :前排膜拜http://blog.csdn.net/qpswwww/artic ...

  4. sass mixin 持续更新

    控制多行显示省略号... //文字溢出省略号@mixin coveText($num:1){ @if $num == 1{ white-space: normal; overflow: hidden; ...

  5. 自动设置 rem es模块写法

    export default function () { let html = document.documentElement; function onWindowResize() { if (ht ...

  6. poj 2367 拓扑排序入门

    Description The system of Martians' blood relations is confusing enough. Actually, Martians bud when ...

  7. 牛客小白月赛4——I—合唱队形

    链接:https://www.nowcoder.com/acm/contest/134/I来源:牛客网 题目描述 铁子的班级在毕业晚会有一个合唱节目,到了毕业晚会的时候,他们必须排成一排一起合唱&qu ...

  8. 关于js的addEventListener 和一些常用事件

    element.addEventListener(<event-name>, <callback>, <use-capture>);document.addEven ...

  9. 深入解析Linux内核I/O剖析(open,write实现)

    Linux内核将一切视为文件,那么Linux的文件是什么呢?其既可以是事实上的真正的物理文件,也可以是设备.管道,甚至还可以是一块内存.狭义的文件是指文件系统中的物理文件,而广义的文件则可以是Linu ...

  10. linux free 命令 ,讲解得比较好

    解释一下Linux上free命令的输出. 下面是free的运行结果,一共有4行.为了方便说明,我加上了列号.这样可以把free的输出看成一个二维数组FO(Free Output).例如: FO[2][ ...