GCD学习回顾

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
  1. dispatch_source_create, 创建后处于suspending状态,这个时候已经在接收事件了,但是不处理。
  2. 配置dispatch source
    1. 安装event handler
    2. 可选安装cancel handler,这个在文件描述符,port类型的source是必须的,在source释放之前保证关闭掉打开的描述符,端口。
  3. 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;
}

 

 

 
posted @ 2015-09-11 17:25  怎么也得过啊  阅读(224)  评论(0编辑  收藏  举报