block访问的外部变量都会被COPY一份到block自己在heap中分配的数据结构中。所以访问scalar变量没问题,简单的值COPY,但是尽量不要访问大的struct或者由外部语境创建和销毁的对象,因为在block执行的时候,对象可能已经不见了。(weak reference可以避免这个问题)。
- 串行队列取代lock,将对共享资源的访问异步串行化。很像erlang的收件箱概念。
- 串行队列的创建没有什么资源消耗,可以创建很多,但是如果同时有很多串行队列任务在同时执行,说明设计有问题,如果很多任务需要并行执行,最好统一交给global concurrent queue去管理。
- dispatch objects也是引用计数的,在ARC环境下,当作用域消失之后,会被自动析构,但是如果作用域消失的时候,如果队列还有任务没有完成,则要等到任务都结束之后才会被析构掉。每个任务都会引用队列的。
- dispatch_sync & dispatch_apply都是同步函数,所以要确保使用的队列不是当前线程的队列,否则死锁。
- dispatch_group_t 可以用来创建一组任务来并行完成一项工作,创建group任务的线程可以等待任务都完成了,然后继续执行。
dispatch source
当和系统底层打交道的时候是比较耗时的,可以采用dispatch source来等待底层完成工作之后通知回调。
source type:
- 定时器定时通知
- unix signal
- 文件描述符,文件句柄或者网络句柄变化通知:例如文件属性变化,网络数据到达
- 设备端口
- 进程
- 自己定制的
dispatch source可能会产生大量的事件,为了防止过度干扰,会合并这些事件。如果之前产生的事件没有被event handler进行处理,那么就会被新产生的事件合并掉。合并的策略根据source的类型而不同。例如:signal-based source会只保留最后一个事件,但是会表述自从最后一次event handler被调用之后,一共来过多少个siginal事件。timer-based source会只保留最后一个事件。
创建dispatch source
- dispatch_source_create, 创建后处于suspending状态,这个时候已经在接收事件了,但是不处理。
- 配置dispatch source
- 安装event handler
- 可选安装cancel handler,这个在文件描述符,port类型的source是必须的,在source释放之前保证关闭掉打开的描述符,端口。
- dispatch_resume,激活source处理事件, 这个方法调用时source必须处于suspended状态,否则CRASH是必然的。dispatch_suspend会将source的suspend count+1, dispatch_resume则-1,当suspend count > 0,则当当前正在执行的block完成之后进入suspend状态。当=0的时候,则恢复运行,继续执行。这两个方法必须balance(不知道如何翻译准确),也就是调用dispatch_suspend的时候,必须之前是resumed状态,反之依然。否则CRASH是必然的。
用信号量来控制有限的资源访问
dispatch_semaphore_create 创建一个信号量,并指明最大可以获得资源数量。
dispatch_semaphore_wait会将信号量-1,如果信号量变成了负值,则阻塞当前线程,直到信号量变为正值。
dispatch_semaphore_signal会将信号量+1
官方的一个关于控制同时打开文件描述符的例子。
// Create the semaphore, specifying the initial pool size dispatch_semaphore_t fd_sema = dispatch_semaphore_create(getdtablesize() / 2); // Wait for a free file descriptor dispatch_semaphore_wait(fd_sema, DISPATCH_TIME_FOREVER); fd = open("/etc/services", O_RDONLY); // Release the file descriptor when done close(fd); dispatch_semaphore_signal(fd_sema);
dispatch_queue的任务共享变量
dispatch_get_context, dispatch_set_context,
任务共享变量首先不是线程安全的,并行队列应该没什么使用价值吧?
#import <Foundation/Foundation.h> typedef struct _ContextData { int number; } ContextData; @interface ZNContextData : NSObject @property (nonatomic) int number; @end @interface ZNQueueContext : NSObject /** * 运行基于C结构的context例子 */ - (void) runStructContextSample; /** * 运行基于对象的context例子 */ - (void)runObjectContextSample; @end #import "ZNQueueContext.h" @implementation ZNContextData - (void)dealloc { NSLog(@"deallocating %@", self.class); } @end @implementation ZNQueueContext - (void) runStructContextSample { dispatch_queue_t queue = dispatch_queue_create("com.structqueue", DISPATCH_QUEUE_CONCURRENT); //创建context ContextData *data = malloc(sizeof(ContextData)); data->number = 10; //为队列设置context dispatch_set_context(queue, data); dispatch_set_finalizer_f(queue, finalizeForStruct); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //执行10遍 dispatch_apply(100, queue, ^(size_t t) { ContextData *data = (ContextData *)dispatch_get_context(queue); NSLog(@"current number: %d", data->number); data->number += 10; }); }); } - (void)runObjectContextSample { dispatch_queue_t queue = dispatch_queue_create("com.objectqueue", DISPATCH_QUEUE_SERIAL); ZNContextData *data = [[ZNContextData alloc] init]; data.number = 10; //这里讲对象管理权从ARC中接管过来,如果不接管,异步执行的时候,data早就被释放了 dispatch_set_context(queue, (__bridge_retained void*)data); dispatch_set_finalizer_f(queue, finalizeForObject); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //执行10遍 dispatch_apply(100, queue, ^(size_t t) { //这里只涉及类型转换,不涉及对象管理权的变化 ZNContextData *data = (__bridge ZNContextData *) dispatch_get_context(queue); NSLog(@"current number: %d", data.number); data.number += 10; }); }); } #pragma mark - c functions /** * 清除和释放struct * * @param context <#context description#> */ void finalizeForStruct(void *context) { ContextData *data = (ContextData *)context; data->number = 0; free(data); } void finalizeForObject(void *context) { //这里将对象的管理权交换给ARC管理 __unused ZNContextData *data = (__bridge_transfer ZNContextData *)context; }