大话 iOS 多线程操作
多线程技术之:GCD
任务
任务是一个比较抽象的概念,可以简单的认为是一个操作、一个函数、一个方法等等,在实际的开发中大多是以block(block使用详见)的形式,使用起来也更加灵活
队列
1.串行队列
任务按照加入到队列中的顺序执行,遵循FIFO 原则。
2.并行队列
任务按照加入到队列中的顺序执行,遵循FIFO 原则。(相对于同步异步而言,任务的取出还是按照这个规则)
3.全局队列
由系统分配的全局并行队列。
4.主队列
程序执中唯一条主队列,也是串行队列
同步
不会开启新的线程,在当前线程执行,易导致阻塞。
异步
gcd管理的线程池中有空闲线程就会从队列中取出任务执行,会开启线程,也可能从空闲的线程中调取使用。
几种组合类型
线程队列模式.png下面分析一段代码:
-(void)GCDFunc{
// 自定义一个串行队列
dispatch_queue_t serailQueue = dispatch_queue_create("this.is.a.serail.queue", DISPATCH_QUEUE_SERIAL);
// 自定义一个并行队列
dispatch_queue_t concruuentQueue = dispatch_queue_create("this.is.a.coruuent.queue", DISPATCH_QUEUE_CONCURRENT);
//获取全局的并行队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
//获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
/***********************************************************************************/
NSLog(@"task%d当前线程:%@",1,[NSThread currentThread]);
// 异步 并行
dispatch_async(globalQueue, ^{
NSLog(@"xxxxxx%d当前线程:%@",1,[NSThread currentThread]);
});
// 异步 并行
dispatch_async(globalQueue, ^{
NSLog(@"xxxxxx%d当前线程:%@",2,[NSThread currentThread]);
});
//异步 并行队列执行,
dispatch_async(concruuentQueue, ^{
NSLog(@"task%d当前线程:%@",2,[NSThread currentThread]);
//异步,主队列
dispatch_async(mainQueue, ^{
NSLog(@"task%d当前线程:%@",3,[NSThread currentThread]);
});
NSLog(@"task%d当前线程:%@",4,[NSThread currentThread]);
});
//异步串行队列执行
dispatch_async(serailQueue, ^{
NSLog(@"task%d当前线程:%@",5,[NSThread currentThread]);
dispatch_async(serailQueue, ^{
NSLog(@"task%d当前线程:%@",6,[NSThread currentThread]);
});
NSLog(@"task%d当前线程:%@",7,[NSThread currentThread]);
});
dispatch_async(serailQueue, ^{
NSLog(@"task%d当前线程:%@",8,[NSThread currentThread]);
});
//异步 串行 主队列
dispatch_async(mainQueue, ^{
NSLog(@"task%d当前线程:%@",9,[NSThread currentThread]);
});
NSLog(@"task%d当前线程:%@",10,[NSThread currentThread]);
/***********************************************************************************/
[NSThread sleepForTimeInterval:2];
NSLog(@"task%d当前线程:%@",11,[NSThread currentThread]);
//同步 并行
dispatch_sync(concruuentQueue, ^{
NSLog(@"task%d当前线程:%@",12,[NSThread currentThread]);
});
NSLog(@"task%d当前线程:%@",13,[NSThread currentThread]);
//同步 串行
dispatch_sync(serailQueue, ^{
NSLog(@"task%d当前线程:%@",14,[NSThread currentThread]);
});
NSLog(@"task%d当前线程:%@",15,[NSThread currentThread]);
/***********************************************************************************/
// 上面看出同步的方法没多大意思,基本都是顺序执行。。。。。而且都在主线程上。
// 异步:因为必定会开启新的线程,线程的申请是由异步负责,起到开分支的作用;
// 2)串行队列 :会新建线程,只开一条线程 一条线程就够了;
// 但是,如果在主队列中执行异步操作的话,是不会开启新线程的。
// 串行队列,同步执行-----串行队列意味着顺序执行,同步执行意味着不开启线程(在当前线程执行)
// 串行队列,异步执行-----串行队列意味着任务顺序执行,异步执行说明要开线程, (如果开多个线程的话,不能保证串行队列顺序执行,所以只开一个线程)
// 并行队列,异步执行-----并行队列意味着执行顺序不确定,异步执行意味着会开启线程,而并行队列又允许不按顺序执行,所以系统为了提高性能会开启多个线程,来队列取任务(队列中任务取出仍然是顺序取出的,只是线程执行无序)。
// 并行队列,同步执行-----同步执行意味着不开线程,则肯定是顺序执行
// 死锁-----程序执行不出来(死锁) ;
//
//同步 主队列 造成死锁现象
// dispatch_sync(mainQueue, ^{
// NSLog(@"task%d当前线程:%@",12,[NSThread currentThread]);
//
// });
/***********************************************************************************/
图解分析代码执行过程:
WechatIMG27.jpeg死锁
1.主线程发生死锁:
这种死锁最常见,问题也最严重,会造成主线程卡住。原因:主队列,同步执行阻塞当前线程:一直执行第一个任务直到结束。两者互相等待造成死锁,示例如下:
- (void)mainThreadDeadLockTest {
NSLog(@"begin");
dispatch_sync(dispatch_get_main_queue(), ^{
// 发生死锁下面的代码不会执行
NSLog(@"middle");
});
// 发生死锁下面的代码不会执行,当然函数也不会返回,后果也最为严重
NSLog(@"end");
}
2.子线程发生死锁:
原因:serialQueue为串行队列,当代码执行到block1时正常,执行到dispatch_sync时,dispatch_sync等待block2执行完毕才会返回,而serialQueue是串行队列,它正在执行block1,只有等block1执行完毕后才会去执行block2,相互等待造成死锁,测试也好造成程序崩溃。。
- (void)deadLockTest {
// 其它线程的死锁
dispatch_queue_t serialQueue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
// 串行队列block1
NSLog(@"begin");
dispatch_sync(serialQueue, ^{
// 串行队列block2 发生死锁,下面的代码不会执行
NSLog(@"middle");
});
// 不会打印
NSLog(@"end");
});
// 函数会返回,不影响主线程
NSLog(@"return");
}
线程间通讯
1 .一个线程传递数据给另一个线程(最常见的子线程下载数据,主线程刷新数据)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"donwload---%@", [NSThread currentThread]);
// 1.子线程下载图片
NSURL *url = [NSURL URLWithString:@"http://d.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 2.回到主线程设置图片
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"setting---%@ %@", [NSThread currentThread], image);
[self.button setImage:image forState:UIControlStateNormal];
});
});
2 .在一个线程中执行完特定任务后,转到另一个线程继续执行任务
dispatch_semaphore_t signal = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(5);
NSLog(@"我这边完事了。。。。,该你了老哥。。。");
dispatch_semaphore_signal(signal);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
int a = 100;
while (a) {
NSLog(@"隔壁老王完事没。。。。。");
a--;
}
dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
NSLog(@"你完事了该我了。。。。。。。");
});
上面这个例子很好的解释了一个线程任务结束通知另一线程怼上去继续干。。哈哈。。
线程同步
线程同步不等于线程锁。
要解决同步问题,首先需要了解为什么需要线程同步,线程不同步主要原因在于多个线程可能同时操作某个对象从而导致状态不一致的问题。是不是可以这么理解,如果多线程不会同一时刻访问对象就解决了同步问题。如何做到这一点?可以采用串行队列的思想,何为串行队列?可以简单理解为所有操作都必须按顺序依次执行。主线程就是串行队列,最简单的同步方式就是把同步操作放到主线程执行,然并卵,当我没说
既然不能在主线程执行,放到子线程不就OK?创建子线程的方式有很多,不累述,有兴趣的童鞋可以各显神通
1.dispatch_group实现线程同步的方式
dispatch_queue_t queue = dispatch_queue_create("oliver.com", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"task 1 on %@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"task 2 on %@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"task 3 on %@",[NSThread currentThread]);
});
1.通知Group中的任务都执行完毕
dispatch_group_notify(group, queue, ^{
NSLog(@"all task finish");
});
2.阻塞式的等待Group中的任务都执行完毕,可设置等待时间,超过指定时间执行下步。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"since all done , I move on");
dispatch_group_notify 和 dispatch_group_wait 的用法如下:
dispatch_queue_t queue1 = dispatch_queue_create("oliver.com", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
//这些任务可以放到不同的队列中去执行
NSLog(@"start");
dispatch_group_async(group, queue1, ^{
sleep(2);
NSLog(@"task1 finish");
});
dispatch_group_async(group, queue2, ^{
sleep(3);
NSLog(@"task2 finish");
});
dispatch_group_notify(group, queue2, ^{
NSLog(@"上面的任务完成了。。。");
});
dispatch_async(queue2, ^{
NSLog(@"我不在任务group中。。。。。");
});
// dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC));//会阻塞当前线程。如果时间设置很大任务没能完成,会影响用户体验,可以把这放到另一个线程中去。
// NSLog(@"时间到了。。。。");
dispatch_async(queue1, ^{
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC));
NSLog(@"时间到了。。。。");
});
dispatch_async(queue2, ^{
NSLog(@"我的任务测试有没有被阻塞。。。。。");
});
正如注释中讲到的 dispatch_group_wait 会阻塞当前线程 一般如果要使用可以把它放到别的线程中去,但不建议使用,如果没有特殊需求,dispatch_group_notify 的亲和性更好些,不会阻塞当前线程,别的任务也会继续执行,当放入group 中的所有任务执行完毕后,会发出同步信号。
3.dispatch_group_enter、dispatch_group_leave示例
dispatch_group_t group1 = dispatch_group_create();
dispatch_group_enter(group1);
dispatch_async(queue2, ^{
sleep(3);
NSLog(@"task 1 finish");
dispatch_group_leave(group1);
});
dispatch_group_enter(group1);
dispatch_async(queue2, ^{
sleep(1);
NSLog(@"task 2 finish");
dispatch_group_leave(group1);
});
dispatch_group_enter(group1);
dispatch_async(queue2, ^{
sleep(1.5);
NSLog(@"task 3 finish");
dispatch_group_leave(group1);
});
dispatch_group_notify(group1, queue2, ^{
NSLog(@"all task finish");
});
dispatch_group_enter 和 dispatch_group_leave 必须成对出现,否则程序的notify 不能正常检测任务是否完成了。这个和上面的直接加入group功能一样,只不过这个需要手动管理。
2.信号量实现线程同步
1.生产者和消费者问题:我们就可以方便的利用信号量来解决这个问题。
信号量:就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。
其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。
dispatch_semaphore_t signal = dispatch_semaphore_create(1);
if (!signal) {
return;
}
//生产者队列
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (true) {
sleep(1);
// 超时时间设置为DISPATCH_TIME_FOREVER, 表示一直等待, 其后语句被阻塞, 一直等到其被唤醒
// DISPATCH_TIME_NOW, 表示不等待, 继续执行其后面的语句
// 也可以指定时间, dispatch_time(DISPATCH_TIME_NOW, 10*NSEC_PER_SEC)), 表示10s内等待, 超过10s, 继续执行下面语句
dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
NSLog(@"我生产了一个蛋糕。。。。。");
dispatch_semaphore_signal(signal);
}
});
//消费者队列
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (true) {
sleep(1.5);
dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
NSLog(@"我吃了一个蛋糕。。。。。");
dispatch_semaphore_signal(signal);//返回非0值表示成功唤醒线程, 0表示没有唤醒.
}
});
2.模拟网络请求过程:
dispatch_semaphore_t sigal = dispatch_semaphore_create(0);//创建一个信号
NSString * url = [BASE_URL stringByAppendingString:MOVIE_URL];
[ASNetTool postNetworkRequestWithUrlString:url parameters:nil isCache:NO succeed:^(id data) {
DLog(@"%@",data);
[_table.mj_header endRefreshing];
[_dataSource removeAllObjects];
if ([data[@"code"] intValue] == 200 && [data[@"result"] isEqualToString:@"success"]) {
for (NSDictionary * movDic in data[@"data"]) {
MovieModel * model = [[MovieModel alloc] init];
[model setValuesForKeysWithDictionary:movDic];
[_dataSource addObject:model];
}
[self.table reloadData];
dispatch_semaphore_signal(sigal);
}
} fail:^(NSString *error) {
DLog(@"%@",error);
// sleep(10);
dispatch_semaphore_signal(sigal);
}];
//阻塞当前线程5秒等待网络数据完成在操作。。。
dispatch_semaphore_wait(sigal, dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC));
NSLog(@" 下面的操作。。。。");
信号量创建的时候, 可以给他指定一个值.dispatch_semaphore_signal(sem)对信号进行+1操作.dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)对信号进行-1操作.当进行-1时,如果发现信号结果会小于0,那么线程进入阻塞状态.只有当信号>=0才能通过.
那么上面的代码段就容易明白了: 一直等到一个异步的网络请求结束,才继续执行 NSLog(@" 下面的操作。。。。"),也是他的逻辑
3.Barrier 实现线程同步
相比上面两种方式,Barrier用的相对少一些.但是Barrier用起来相对上面两种更加简单.
dispatch_queue_t queue = dispatch_queue_create("oliver.com", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"task 1 on %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"task 2 on %@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"barrier ==========");
});
dispatch_async(queue, ^{
NSLog(@"task 3 on %@",[NSThread currentThread]);
});
上面的代码,task 1和task 2会并发执行,然后执行barrier,下面代码相当于被阻断直到上面代码执行完毕,最后是task 3。
这个 barrier就相当于一个栅栏,将不同的任务区分开来.从代码中也不难看出,这个barrier函数不需要依赖其它的变量,没有侵入性.所以非常好用.和Group也是非常好搭配.例如下面的代码:
dispatch_queue_t queue = dispatch_queue_create("oliver.com", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
for (int i = 0 ; i < 500000000; i++) {
}
NSLog(@"task 1 on %@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"======");
});
dispatch_group_async(group, queue, ^{
for (int i = 0 ; i < 500000000; i++) {
}
NSLog(@"task 2 on %@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"======");
});
dispatch_group_async(group, queue, ^{
NSLog(@"task 3 on %@",[NSThread currentThread]);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"all task done");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"since all done , I move on");
能保证 task 1 2 3顺序执行,同时,由于使用了Group,也能知道执行结束的时机. 但是仅仅是为了说明问题,如果要顺序执行,那么还是使用GCD中同步队列更加合适.没必要这样搞。
4.@synchronized 实现线程同步
NSMutableArray * testArr = @[@"test"].mutableCopy;
//写
dispatch_async(dispatch_get_global_queue(0, 0), ^{
@synchronized (testArr) {//锁对象为数组
for (int i = 0; i<100; i++) {
[testArr addObject:@"aaaaa"];
}
}
});
//写
dispatch_async(dispatch_get_global_queue(0, 0), ^{
@synchronized (testArr) {
for (int i = 0; i<100; i++) {
[testArr addObject:@"bbbb"];
}
}
});
// 读
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(2);//这里要阻塞个一秒,不然可能看不到效果
@synchronized (testArr) {
for (NSString * str in testArr) {
NSLog(@"%@",str);
}
}
});
@synchronized(obj)指令使用的obj为该锁的唯一标识,只有当标识相同时,才为满足互斥,
如果线程2中的@synchronized(obj)改为@synchronized(self),那么线程2就不会被阻塞,
@synchronized指令实现锁的优点就是我们不需要在代码中显式的创建锁对象,便可以实现锁的机制,但作为一种预防措施,@synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象。
5.NSLock 实现线程同步
NSLock * lock = [[NSLock alloc] init];
NSMutableArray * testArr = @[@"test"].mutableCopy;
//写
dispatch_async(dispatch_get_global_queue(0, 0), ^{
if ([lock tryLock]) { //尝试获取锁,如果获取不到返回NO,不会阻塞该线程
for (int i = 0; i<100; i++) {
[testArr addObject:@"aaaa"];
}
[lock unlock];
}else{
NSLog(@"lock cannot use now");
}
});
//写
dispatch_async(dispatch_get_global_queue(0, 0), ^{
if ([lock tryLock]) {
for (int i = 0; i<100; i++) {
[testArr addObject:@"bbbb"];
}
[lock unlock];
}else{
NSLog(@"lock cannot use now");
}
});
//读
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(2);
if ([lock tryLock]) {
for (NSString * str in testArr) {
NSLog(@"%@",str);
}
[lock unlock];
}else{
NSLog(@"lock busy......");
}
});
NSLock是Cocoa提供给我们最基本的锁对象,这也是我们经常所使用的,除lock和unlock方法外,NSLock还提供了tryLock和lockBeforeDate:两个方法,前一个方法会尝试加锁,如果锁不可用(已经被锁住),并不会阻塞线程,并返回NO。lockBeforeDate:方法会在所指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO。
官方源码:
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
@interface NSConditionLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
6.NSRecursiveLock递归锁
// NSLock *lock = [[NSLock alloc] init];
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"value = %d", value);
sleep(1);
RecursiveMethod(value - 1);
}
[lock unlock];
};
RecursiveMethod(5);
});
这段代码若果用NSLock是一个典型的死锁情况。在我们的线程中,RecursiveMethod是递归调用的。如果使用NSLock,每次进入这个block时,都会去加一次锁,而从第二次开始,由于锁已经被使用了且没有解锁,所以它需要等待锁被解除,这样就导致了死锁,线程被阻塞住了。调试器中会输出如下信息:
2017-07-15 17:33:36.168 LeeSDWebImageLearn[2500:154959] value = 5
2017-07-15 17:33:37.174 LeeSDWebImageLearn[2500:154959] -[NSLock lock]: deadlock (<NSLock: 0x6080000cbf30> '(null)')
2017-07-15 17:33:37.174 LeeSDWebImageLearn[2500:154959] Break on _NSLockError() to debug.
在这种情况下,我们就可以使用NSRecursiveLock。它可以允许同一线程多次加锁,而不会造成死锁。递归锁会跟踪它被lock的次数。每次成功的lock都必须平衡调用unlock操作。只有所有达到这种平衡,锁最后才能被释放,以供其它线程使用。
7.NSConditionLock条件锁
NSConditionLock *lock = [[NSConditionLock alloc] init];
NSMutableArray *products = [NSMutableArray array];
NSInteger HAS_DATA = 1;
NSInteger NO_DATA = 0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
[lock lockWhenCondition:NO_DATA];
[products addObject:[[NSObject alloc] init]];
NSLog(@"produce a product,总量:%zi",products.count);
[lock unlockWithCondition:HAS_DATA];
sleep(1);
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(@"wait for product");
[lock lockWhenCondition:HAS_DATA];
[products removeObjectAtIndex:0];
NSLog(@"custome a product");
[lock unlockWithCondition:NO_DATA];
}
});
当我们在使用多线程的时候,有时一把只会lock和unlock的锁未必就能完全满足我们的使用。因为普通的锁只能关心锁与不锁,而不在乎用什么钥匙才能开锁,而我们在处理资源共享的时候,多数情况是只有满足一定条件的情况下才能打开这把锁:
在开始的时候中的加锁的线程1可以很容易就锁住了,但在unlock的使用了一个整型的条件,它可以开启其它线程中正在等待这把钥匙的临界地,而线程2则需要一把被标识为2的钥匙,所以当线程1循环到最后一次的时候,才最终打开了线程2中的阻塞。但即便如此,NSConditionLock也跟其它的锁一样,是需要lock与unlock对应的,只是lock,lockWhenCondition:与unlock,unlockWithCondition:是可以随意组合的,当然这是与你的需求相关的。
8.NSCondition
NSCondition *condition = [[NSCondition alloc] init];
NSMutableArray *products = [NSMutableArray array];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
[condition lock];
if ([products count] == 0) {
NSLog(@"没有蛋糕了。。。");
[condition wait];
}
[products removeObjectAtIndex:0];
NSLog(@"A 吃了一个。。。");
[condition unlock];
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
[condition lock];
if ([products count] == 0) {
NSLog(@"没有蛋糕了。。。");
[condition wait];
}
[products removeObjectAtIndex:0];
NSLog(@"B 吃了一个。。。。。");
[condition unlock];
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
[condition lock];
[products addObject:[[NSObject alloc] init]];
NSLog(@"生产了%lu个蛋糕",(unsigned long)products.count);
[condition signal];
[condition unlock];
sleep(1);
}
});
一种最基本的条件锁。手动控制线程wait和signal。
[condition lock];一般用于多线程同时访问、修改同一个数据源,保证在同一时间内数据源只被访问、修改一次,其他线程的命令需要在lock 外等待,只到unlock ,才可访问[condition unlock];
与lock 同时使用[condition wait];让当前线程处于等待状态
[condition signal];CPU发信号告诉线程不用在等待,可以继续执行。
9.C 语言 pthread_mutex 互斥锁
__block pthread_mutex_t theLock;
pthread_mutex_init(&theLock, NULL);
NSMutableArray * arr = @[@"test"].mutableCopy;
//写入
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&theLock);
for (int i = 0; i < 100; i++) {
[arr addObject:@"test"];
}
pthread_mutex_unlock(&theLock);
});
//写入
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&theLock);
for (int i = 0; i < 100; i++) {
[arr addObject:@"oliver"];
}
pthread_mutex_unlock(&theLock);
});
//读取
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
pthread_mutex_lock(&theLock);
for (NSString * str in arr) {
NSLog(@"%@",str);
}
pthread_mutex_unlock(&theLock);
});
//c语言定义下多线程加锁方式。
//
//pthread_mutex_init(pthread_mutex_t mutex,const pthread_mutexattr_t attr);初始化锁变量mutex。attr为锁属性,NULL值为默认属性。
//pthread_mutex_lock(pthread_mutex_t *mutex);加锁
//pthread_mutex_tylock(pthread_mutex_t *mutex);加锁,但是与2不一样的是当锁已经在使用的时候,返回为EBUSY,而不是挂起等待。
//pthread_mutex_unlock(pthread_mutex_t *mutex);释放锁
//pthread_mutex_destroy(pthread_mutex_t *mutex);使用完后释放
10.pthread_mutex(recursive) 递归锁
__block pthread_mutex_t theLock;
//pthread_mutex_init(&theLock, NULL);
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&theLock, &attr);
pthread_mutexattr_destroy(&attr);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
pthread_mutex_lock(&theLock);
if (value > 0) {
NSLog(@"value = %d", value);
sleep(1);
RecursiveMethod(value - 1);
}
pthread_mutex_unlock(&theLock);
};
RecursiveMethod(5);
});
这是pthread_mutex为了防止在递归的情况下出现死锁而出现的递归锁。作用和NSRecursiveLock递归锁类似。
如果使用pthread_mutex_init(&theLock, NULL);初始化锁的话,上面的代码会出现死锁现象。如果使用递归锁的形式,则没有问题。
11.OSSpinLock自旋锁
__block OSSpinLock theLock = OS_SPINLOCK_INIT;
NSMutableArray * arr = @[@"test"].mutableCopy;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&theLock);
for (int i =0; i < 100; i++) {
[arr addObject:@"aaaa"];
}
OSSpinLockUnlock(&theLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&theLock);
for (int i =0; i < 100; i++) {
[arr addObject:@"bbbb"];
}
OSSpinLockUnlock(&theLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
OSSpinLockLock(&theLock);
for (NSString * str in arr) {
NSLog(@"%@",str);
}
OSSpinLockUnlock(&theLock);
});
OSSpinLock 自旋锁,性能最高的锁。原理很简单,就是一直 do while 忙等。它的缺点是当等待时会消耗大量 CPU 资源,所以它不适用于较长时间的任务。 不过最近YY大神在自己的博客不再安全的 OSSpinLock中说明了OSSpinLock已经不再安全,请大家谨慎使用。
最后来看一个性能分析:
屏幕快照 2017-07-15 下午5.19.15.pngNSOperation的使用
配合使用NSOperation和NSOperationQueue也能实现多线程编程
NSOperation和NSOperationQueue实现多线程的具体步骤
- 先将需要执行的操作创建到一个NSOperation对象中
- 然后将NSOperation对象添加到NSOperationQueue中
- 系统会自动将NSOperationQueue中的NSOperation取出来
- 将取出的NSOperation封装的操作放到一条新线程中执行
注意: - NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
- 使用NSOperation子类的方式有3种:
1.NSInvocationOperation
2.NSBlockOperation
3.自定义子类继承NSOperation,实现内部相应的方法
NSBlockOperation使用:
- 创建NSBlockOperation对象
+(id)blockOperationWithBlock:(void(^)(void))block; 类方法创建一个操作
- 通过addExecutionBlock:方法添加更多的操作
-(void)addExecutionBlock:(void(^)(void))block;
下面看demo:
@synthesize testArray = _testArray;
//实现一个线程安全的数组读写操作,
-(void)setTestArray:(NSMutableArray *)testArray{
@synchronized (self) {
if (_testArray!=testArray) {
_testArray = testArray;
}
}
}
-(NSMutableArray*)testArray{
@synchronized (self) {
if (!_testArray) {
_testArray = [NSMutableArray array];
}
return _testArray;
}
}
具体的操作:
NSBlockOperation * op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1------%@", [NSThread currentThread]);
for (int i =0; i<20; i++) {
[self.testArray addObject:@"test"];
NSLog(@"adddddd");
}
}];
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2------%@", [NSThread currentThread]);
if (self.testArray.count>0) {
[self.testArray removeObjectAtIndex:0];
}
}];
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3------%@", [NSThread currentThread]);
for (int i =0; i<20; i++) {
[self.testArray addObject:@"oliverllee"];
NSLog(@"add");
}
}];
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"4------%@", [NSThread currentThread]);
if (self.testArray.count>0) {
[self.testArray removeObjectAtIndex:0];
}
}];
NSBlockOperation * op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"4------%@", [NSThread currentThread]);
for (int i =0; i<20; i++) {
[self.testArray addObject:@"oliverllee"];
NSLog(@"add");
}
}];
//添加依赖顺序
[op1 addDependency:op];
[op3 addDependency:op1];
[op2 addDependency:op3];
[op4 addDependency:op2];
//创建队列
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 3;
[queue addOperations:@[op,op2,op1,op3,op4] waitUntilFinished:YES];//加入队列的操作会自动执行
// [op start]; //或者调用操作的start 方法
//注意:只要NSBlockOperation封装的操作数 >1,就会异步执行操作
分析上面代码给数组加了把锁,保证读写安全进行,同时生成了5个操作,并设置了执行的依赖顺序,而且设置了最大并发量,并加入了队列中执行,便于管控。然而上面的添加依赖后数组的读写都是有顺序了,看不出效果,读者可以把依赖顺序去掉,让他自由发挥看看效果。。
在看一下这个:
NSBlockOperation * op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1------%@", [NSThread currentThread]);
for (int i =0; i<20; i++) {
[self.testArray addObject:@"test"];
NSLog(@"adddddd");
}
}];
[op addExecutionBlock:^{
NSLog(@"2------%@", [NSThread currentThread]);
sleep(0.5);
if (self.testArray.count>0) {
[self.testArray removeObjectAtIndex:0];
NSLog(@"delete...");
}
}];
[op addExecutionBlock:^{
NSLog(@"3------%@", [NSThread currentThread]);
for (int i =0; i<20; i++) {
[self.testArray addObject:@"oliverllee"];
NSLog(@"add");
}
}];
[op addExecutionBlock:^{
NSLog(@"4------%@", [NSThread currentThread]);
sleep(0.5);
if (self.testArray.count>0) {
[self.testArray removeObjectAtIndex:0];
NSLog(@"delete...");
}
}];
[op addExecutionBlock:^{
NSLog(@"4------%@", [NSThread currentThread]);
for (int i =0; i<20; i++) {
[self.testArray addObject:@"oliverllee"];
NSLog(@"add");
}
}];
//创建队列
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 3;
[queue addOperations:@[op] waitUntilFinished:YES];//加入队列的操作会自动执行
// [op start]; //或者调用操作的start 方法
//注意:只要NSBlockOperation封装的操作数 >1,就会异步执行操作
和上面不同的是这个创建了一个操作,但是在这个操作上添加了很多任务,如果用队列自动执行的时候都在子线程,如果用start 方法,那么第一个会在主线程上执行,其余的在子线程上执行任务。
注意:maxConcurrentOperationCount 该方法可以控制最大的并发数量,有的同学会说用Queue 没法实现串行队列,你可以把这个值设置成1,那么这样不就相当于在一个串行队列中执行任务了吗,但是可能是几个线程,那要看操作系统调度分配了
继续上面的demo 把最大并发改成1:
//创建队列
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;
[queue addOperations:@[op,op2,op1,op3,op4] waitUntilFinished:YES];//加入队列的操作会自动执行
控制台打印:
2017-07-16 13:39:31.747 LeeSDWebImageLearn[2294:125913] 1------<NSThread: 0x608000261640>{number = 3, name = (null)}
2017-07-16 13:39:31.748 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.748 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.749 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.749 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.750 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.750 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.751 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.751 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.751 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.752 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.753 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.753 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.755 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.755 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.755 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.755 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.756 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.756 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.756 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.756 LeeSDWebImageLearn[2294:125913] adddddd
2017-07-16 13:39:31.757 LeeSDWebImageLearn[2294:125924] 3------<NSThread: 0x60000007fc00>{number = 4, name = (null)}
2017-07-16 13:39:31.757 LeeSDWebImageLearn[2294:125924] add
可以看出的确实现了串行效果,没必要这样做的。。。
- 暂停和恢复以及取消:
//创建队列
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;
self.queue = queue;
[queue addOperations:@[op,op2,op1,op3,op4] waitUntilFinished:NO];//加入队列的操作会自动执行
// [op start]; //或者调用操作的start 方法
//注意:只要NSBlockOperation封装的操作数 >1,就会异步执行操作
}
-(void)btnClick:(UIButton*)sender{
self.queue.suspended = !self.queue.suspended;//暂停恢复队列操作
}
注意:要将上面的waituntilfinish 设置 为 NO 若果是YES 暂停恢复没用的操作
另一个就是取消同样也是要设置为NO才有效果:
self.queue = queue;
[queue addOperations:@[op,op2,op1,op3,op4] waitUntilFinished:NO];//加入队列的操作会自动执行
// [op start]; //或者调用操作的start 方法
//注意:只要NSBlockOperation封装的操作数 >1,就会异步执行操作
}
-(void)btnClick:(UIButton*)sender{
[self.queue cancelAllOperations];
}
NSInvocationOperation的使用:
NSInvocationOperation * op1= [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
NSInvocationOperation * op2= [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
NSInvocationOperation * op3= [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task3) object:nil];
NSInvocationOperation * op4= [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task4) object:nil];
NSInvocationOperation * combineOP = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(combine) object:nil];
[op2 addDependency:op1];
[op3 addDependency:op4];
[op4 addDependency:op2];
[combineOP addDependency:op3];
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[op1,op2,op3,op4] waitUntilFinished:NO];
[[NSOperationQueue mainQueue] addOperation:combineOP];
}
-(void)task1{
NSData * imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:@""]];
sleep(1);
}
-(void)task2{
NSData * imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:@""]];
sleep(1);
}
-(void)task3{
NSData * imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:@""]];
sleep(1);
}
-(void)task4{
NSData * imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:@""]];
sleep(1);
}
-(void)combine{
NSLog(@"start combine");
}
上面是NSInvocationOperation简单的用法 合成一张图片效果演示。
- 线程间通讯:
//1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.使用简便方法封装操作并添加到队列中
[queue addOperationWithBlock:^{
//3.在该block中下载图片
NSURL *url = [NSURL URLWithString:@"http://news.51sheyuan.com/uploads/allimg/111001/133442IB-2.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"下载图片操作--%@",[NSThread currentThread]);
//4.回到主线程刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
NSLog(@"刷新UI操作---%@",[NSThread currentThread]);
}];
}];
自定义NSOperation 的子类:
- 自定义NSOperation的步骤很简单
- 重写-(void)main方法,在里面实现想执行的任务
- 重写-(void)main方法的注意点
- 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
- 经常通过-(BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
- 重写-(void)start方法
使用 main 方法非常简单,开发者不需要管理一些状态属性(例如 isExecuting 和 isFinished),当 main 方法返回的时候,这个 operation 就结束了。这种方式使用起来非常简单,但是灵活性相对重写 start 来说要少一些, 因为main方法执行完就认为operation结束了,所以一般可以用来执行同步任务。
如果你希望拥有更多的控制权,或者想在一个操作中可以执行异步任务,那么就重写 start 方法, 但是注意:这种情况下,你必须手动管理操作的状态, 只有当发送 isFinished 的 KVO 消息时,才认为是 operation 结束
不多说看代码:
//
// LeeOperation.h
// LeeSDWebImageLearn
//
// Created by LiYang on 2017/7/15.
// Copyright © 2017年 LiYang. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
//下载回调协议
@protocol DownloadOperationDelegate <NSObject>
@optional
-(void)downFinishWithImage:(UIImage*)image;
@end
@interface LeeOperation : NSOperation
@property (nonatomic,copy)NSString * imageUrl;
@property (nonatomic,assign)id <DownloadOperationDelegate>delegate;
- (id)initWithUrl:(NSString *)url delegate:(id <DownloadOperationDelegate>)delegate;
//下载完成回调
-(id)initWithUrl:( NSString * )url andFinishiHandler:(void (^) (UIImage * image )) completion FailHanler:(void (^)(NSError * error)) errorHandler;
@end
//
// LeeOperation.m
// LeeSDWebImageLearn
//
// Created by LiYang on 2017/7/15.
// Copyright © 2017年 LiYang. All rights reserved.
//
#import "LeeOperation.h"
@interface LeeOperation(){
void (^finishCallBackBlock)(UIImage * image);
void (^faileCallBackBlock)(NSError * error);
}
@end
@implementation LeeOperation
-(id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate{
if (self = [super init]) {
self.imageUrl = url;
self.delegate = delegate;
}
return self;
}
-(id)initWithUrl:(NSString *)url andFinishiHandler:(void (^)(UIImage * ))completion FailHanler:(void (^)(NSError * ))errorHandler{
if (self = [super init]) {
self.imageUrl = url;
finishCallBackBlock = completion;
faileCallBackBlock = errorHandler;
}
return self;
}
-(void)main{
// 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
@autoreleasepool {
if(self.isCancelled)return;
NSLog(@"已下载 %@",[NSThread currentThread]);
// 获取图片数据
NSURL * url = [NSURL URLWithString:self.imageUrl];
NSData * imageData = [NSData dataWithContentsOfURL:url];
if (self.isCancelled) {
url = nil;
imageData = nil;
return;
}
//得到图片数据
UIImage * image = [UIImage imageWithData:imageData];
if (self.isCancelled) {
image = nil;
return;
}
if ([self.delegate respondsToSelector:@selector(downFinishWithImage:)]) {
[(NSObject*)self.delegate performSelectorOnMainThread:@selector(downFinishWithImage:) withObject:image waitUntilDone:NO];
}
if (finishCallBackBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
finishCallBackBlock(image);
});
}
}
}
@end
控制台效果:
2017-07-16 16:22:40.268 LeeSDWebImageLearn[3763:203593] 已下载 <NSThread: 0x608000066c80>{number = 8, name = (null)}
2017-07-16 16:22:40.268 LeeSDWebImageLearn[3763:203594] 已下载 <NSThread: 0x60000006bf40>{number = 7, name = (null)}
2017-07-16 16:22:40.779 LeeSDWebImageLearn[3763:203349] <UIImage: 0x60800009a310>, {1024, 694}
2017-07-16 16:22:40.810 LeeSDWebImageLearn[3763:203349] <UIImage: 0x60000009d560>, {1024, 694}
下面看下重载start方法的效果:
//
// TestOperation.m
// LeeSDWebImageLearn
//
// Created by LiYang on 2017/7/16.
// Copyright © 2017年 LiYang. All rights reserved.
//
#import "TestOperation.h"
@interface TestOperation()
@property (assign, nonatomic, getter = isExecuting) BOOL executing;
@property (assign, nonatomic, getter = isFinished) BOOL finished;
@property (nonatomic,copy)NSURL * imageUrl;
@property (nonatomic,copy)void(^finishHandler)(UIImage * image);//下载完成的回调
@end
@implementation TestOperation
// 因为父类的属性是Readonly的,重载时如果需要setter的话则需要手动合成
@synthesize executing = _executing;
@synthesize finished = _finished;
-(void)dealloc{
NSLog(@"dealloc");
}
-(id)initWithImageUrl:(NSString *)url andFinishHandler:(void (^)(UIImage *))finishHandler{
if (self = [super init]) {
_executing = NO;
_finished = NO;
_imageUrl = [NSURL URLWithString:url];
_finishHandler = [finishHandler copy];
}
return self;
}
- (void) start {
if ([self isCancelled]){
self.finished = YES;
return;
} else {
// 没有取消,那就要告诉KVO,该队列开始执行了(isExecuting)
self.executing = YES;
/*下面是要执行的代码段*/
NSLog(@"Current Thread = %@", [NSThread currentThread]);
NSData * imageData = [NSData dataWithContentsOfURL:self.imageUrl];
UIImage * image = nil;
if (![self isCancelled]) {
image = [UIImage imageWithData:imageData];
self.finishHandler(image);
}else{
self.finishHandler(image);
}
/*执行的代码段*/
[self done];
}
}
- (void)done {
self.finished = YES;
self.executing = NO;
}
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
-(BOOL)isConcurrent{
return YES;
}
// 3.如果要自定义operation,需要继承NSOperation。并且重载 isExecuting方法和isFinished,start,isconcurrent方法。在这两个方法中,必须返回一个线程安全值(通常是个BOOL值),这个值可以在 operation 中进行操作。
// 4.一旦你的 operation 开始了,必须通过 KVO,告诉所有的监听者,现在该operation的执行状态。
// 6.必须为我们自定义的 operation 提供一个初始化方法
@end
执行完毕后控制台的效果:在子线程中执行任务,执行完毕后对象销毁。
2017-07-16 22:18:19.206 LeeSDWebImageLearn[1277:71873] Current Thread = <NSThread: 0x6000000729c0>{number = 3, name = (null)}
2017-07-16 22:18:19.724 LeeSDWebImageLearn[1277:71873] <UIImage: 0x600000085140>, {1024, 694}
2017-07-16 22:18:19.725 LeeSDWebImageLearn[1277:71873] dealloc
小记:
1. NSOperation + NSOperationQueue
- NSOperation 拥有更多的函数可用,具体可看api
- 在NSOperation 中可以建立几个操作间的依赖关系,控制操作较方便。
- 有kvo,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)
- NSOperationQueue可以方便的管理并发、NSOperation之间的优先级
2. GCD的优势
- 易用: GCD 提供一个易于使用的并发模型而不仅仅只是锁和线程,以帮助我们避开并发陷阱,而且因为基于block,它能极为简单得在不同代码作用域之间传递上下文。
- 灵活: GCD 具有在常见模式上(比如锁、单例),用更高性能的方法优化代码,而且GCD能提供更多的控制权力以及大量的底层函数。
- 性能: GCD能自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率。
demo地址:
未完待续:
友情链接:
死锁:http://ios.jobbole.com/82622/
http://www.dreamingwish.com/article/ios-multithread-program-runloop-the.html
http://www.cnblogs.com/whatbeg/p/4435286.html
http://www.jianshu.com/p/4edf98a61483
https://bestswifter.com/ios-lock/
不错:
http://www.jianshu.com/p/35dd92bcfe8c
http://www.jianshu.com/p/b2a7c985df3e
http://blog.leichunfeng.com/blog/2015/07/29/ios-concurrency-programming-operation-queues/
http://www.jianshu.com/p/65629ba90d6a
https://blog.cnbluebox.com/blog/2014/07/01/cocoashen-ru-xue-xi-nsoperationqueuehe-nsoperationyuan-li-he-shi-yong/
http://www.jianshu.com/p/7649fad15cdb
干:
http://www.jianshu.com/p/3309b0c0cab3