GCD 使用GCD后,可以不用浪费精力去关注线程,你只需要想清楚任务的执行方法(同步还是异步)和队列的运行方式(串行还是并行)即可。
任务是一个抽象概念,表示用来执行的一段代码,可以是一个block或者一个函数
串行/并行和同步/异步 串行/并行 在使用GCD的时候,会把需要处理的任务放到Block中,然后将任务追加到相应的队列里,这个队列就叫Dispatch Queue 。然而,存在于两种Dispatch Queue,一种是要等待上一个执行完,再执行下一个的Serial Dispatch Queue,这叫做串行队列
;另一种,则是不需要上一个执行完,就能执行下一个的Concurrent Dispatch Queue,叫做并行队列
.这两种,均遵循FIFO原则。
队列对应到代码里就是一个dispatch_queue对象:
1 @property (nonatomic , strong )dispatch_queue_t queue;
同步/异步 dispatch_async表示异步,将指定的Block“异步”加入Dispach Queue,不做任何等待
dispatch_sync表示同步,将指定的Block“同步”的加入Dispatch Queue,在Block结束之前,dispatch_sync会一直等待
串行队列死锁 1 2 3 4 5 6 7 NSLog (@"1" ); dispatch_sync (dispatch_get_main_queue(), ^{ NSLog (@"2" ); }); NSLog (@"3" );
分析:
dispatch_sync表示是一个同步线程;
dispatch_get_main_queue表示运行在主线程中的主队列;
任务2是同步线程的任务。
首先执行任务1,这是肯定没问题的,只是接下来,程序遇到了同步线程,那么它会进入等待,等待任务2执行完,然后执行任务3。但这是队列,有任务来,当然会将任务加到队尾,然后遵循FIFO原则执行任务。那么,现在任务2就会被加到最后,任务3排在了任务2前面,问题来了: 任务3要等任务2执行完才能执行,任务2由排在任务3后面,意味着任务2要在任务3执行完才能执行,所以他们进入了互相等待的局面。【既然这样,那干脆就卡在这里吧】这就是死锁。
从这里看发生死锁需要2个条件:
代码运行的当前队列是串行队列
使用sync将任务加入到自己队列中
队列优先级设置
队列创建1 2 3 dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
attr三种传值方式
1 2 3 4 5 6 7 8 9 10 #define DISPATCH_QUEUE_SERIAL NULL #define DISPATCH_QUEUE_CONCURRENT \ DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t, \ _dispatch_queue_attr_concurrent) dispatch_queue_attr_t dispatch_queue_attr_make_with_qos_class(dispatch_queue_attr_t attr, dispatch_qos_class_t qos_class, int relative_priority);
dispatch_queue_attr_make_with_qos_class函数可以创建带有优先级的dispatch_queue_attr_t对象。通过这个对象可以自定义queue的优先级。
NSOperation 和 NSOperationQueue NSOperation是基于GCD开发的,但是比GCD拥有更强的可控性和代码可读性,NSOperation是一个抽象基类,表示一个独立的计算单元,可以为子类提供有用且线程安全的建立状态,优先级,依赖,和取消等操作。使用比较多的就是NSInvocationOperation和NSBlockOperation,更多的是定制
NSInvocationOperation 1 2 3 4 5 6 7 NSInvocationOperation *invo = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector (test:) object:nil ]; [invo start]; NSLog (@"111" ); - (void )test:(NSString *)string { sleep(3 ); NSLog (@"test - %@ - %@" , [NSThread currentThread], string); }
1 2 2017-09-29 14:19:13.242517+0800 aegewgr[10143:3388734] test - <NSThread: 0x600000078180>{number = 1, name = main} - (null) 2017-09-29 14:19:13.242967+0800 aegewgr[10143:3388734] 111
可以看到NSInvocaionOperation是同步并且串行的
,主要还是要和NSOperationQueue结合使用
NSBlockOperation NSBlockOperation支持并发的实行一个或多个block
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 NSBlockOperation *blockOperation = [[NSBlockOperation alloc]init]; [blockOperation addExecutionBlock:^{ NSLog (@"block 1 in thread:%@" ,[NSThread currentThread]); }]; [blockOperation addExecutionBlock:^{ NSLog (@"block 2 in thread:%@" ,[NSThread currentThread]); }]; [blockOperation addExecutionBlock:^{ NSLog (@"block 3 in thread:%@" ,[NSThread currentThread]); }]; [blockOperation addExecutionBlock:^{ NSLog (@"block 4 in thread:%@" ,[NSThread currentThread]); }]; [blockOperation addExecutionBlock:^{ sleep(1 ); NSLog (@"block 5 in thread:%@" ,[NSThread currentThread]); }]; [blockOperation start]; NSLog (@"123" );
1 2 3 4 5 6 2017-09-29 14:32:03.710936+0800 aegewgr[10335:3439694] block 1 in thread:<NSThread: 0x60400006d740>{number = 1, name = main} 2017-09-29 14:32:03.710939+0800 aegewgr[10335:3439916] block 3 in thread:<NSThread: 0x60400027a780>{number = 4, name = (null)} 2017-09-29 14:32:03.710943+0800 aegewgr[10335:3439920] block 4 in thread:<NSThread: 0x60400027a840>{number = 5, name = (null)} 2017-09-29 14:32:03.710961+0800 aegewgr[10335:3439919] block 2 in thread:<NSThread: 0x600000270c00>{number = 3, name = (null)} 2017-09-29 14:32:04.712532+0800 aegewgr[10335:3439920] block 5 in thread:<NSThread: 0x60400027a840>{number = 5, name = (null)} 2017-09-29 14:32:04.712932+0800 aegewgr[10335:3439694] 123
NSBlockOperation也是同步的,而block的执行是并发的
自定义NSOperation 自定义NSOperaion分两种,一种是自定义非并发的NSOperaion,一种是自定义并发的NSOperaion
定义非并发的NSOperaion 只需要重写main方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #import "SerialNSOperation.h" @implementation SerialNSOperation - (void )main { NSLog (@"main begin" ); @try { @autoreleasepool { BOOL taskIsFinished = NO ; while (taskIsFinished == NO && [self isCancelled] == NO ){ NSLog (@"currentThread = %@" , [NSThread currentThread]); sleep(10 ); taskIsFinished = YES ; } } } @catch (NSException * e) { NSLog (@"Exception %@" , e); } NSLog (@"main end" ); }
然后直接使用
1 2 SerialNSOperation *op = [[SerialNSOperation alloc]init]; [op start];
1 2 3 2017-09-29 15:12:59.151481+0800 aegewgr[10524:3564080] main begin 2017-09-29 15:13:09.152082+0800 aegewgr[10524:3564080] currentThread = <NSThread: 0x60400006e1c0>{number = 1, name = main} 2017-09-29 15:13:09.152299+0800 aegewgr[10524:3564080] main end
实用性不大
定义并发的NSOperation
start方法:该方法必须实现,
main:该方法可选,如果你在start方法中定义了你的任务,则这个方法就可以不实现,但通常为了代码逻辑清晰,通常会在该方法中定义自己的任务
isExecuting isFinished 主要作用是在线程状态改变时,产生适当的KVO通知
isAsynchronous :必须覆盖并返回YES;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #import <Foundation/Foundation.h> @interface ConcurrentOperation : NSOperation { BOOL executing; BOOL finished; } #import "ConcurrentOperation.h" @implementation ConcurrentOperation - (id )init { if (self = [super init]) { executing = NO ; finished = NO ; } return self ; } - (BOOL )isAsynchronous { return YES ; } - (BOOL )isExecuting { return executing; } - (BOOL )isFinished { return finished; } - (void )start { if ([self isCancelled]) { [self willChangeValueForKey:@"isFinished" ]; finished = YES ; [self didChangeValueForKey:@"isFinished" ]; return ; } [self willChangeValueForKey:@"isExecuting" ]; [NSThread detachNewThreadSelector:@selector (main) toTarget:self withObject:nil ]; executing = YES ; [self didChangeValueForKey:@"isExecuting" ]; } - (void )main { NSLog (@"main begin" ); @try { @autoreleasepool { NSLog (@"自定义并发操作NSOperation" ); NSThread *thread = [NSThread currentThread]; NSLog (@"current Thread:%@" ,thread); [self willChangeValueForKey:@"isFinished" ]; [self willChangeValueForKey:@"isExecuting" ]; executing = NO ; finished = YES ; [self didChangeValueForKey:@"isExecuting" ]; [self didChangeValueForKey:@"isFinished" ]; } } @catch (NSException * e) { NSLog (@"Exception %@" , e); } NSLog (@"main end" ); }
1 2 3 4 5 6 7 8 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; ConcurrentOperation *op1 = [[ConcurrentOperation alloc]init]; ConcurrentOperation *op2 = [[ConcurrentOperation alloc]init]; ConcurrentOperation *op3 = [[ConcurrentOperation alloc]init]; [queue addOperation:op1]; [queue addOperation:op2]; [queue addOperation:op3];
1 2 3 4 5 6 7 8 9 10 11 12 2017 -09 -29 15 :34 :15.158649 +0800 aegewgr[10664 :3638228 ] main begin2017 -09 -29 15 :34 :15.158653 +0800 aegewgr[10664 :3638226 ] main begin2017 -09 -29 15 :34 :15.158675 +0800 aegewgr[10664 :3638227 ] main begin2017 -09 -29 15 :34 :15.158912 +0800 aegewgr[10664 :3638228 ] 自定义并发操作NSOperation 2017 -09 -29 15 :34 :15.159321 +0800 aegewgr[10664 :3638226 ] 自定义并发操作NSOperation 2017 -09 -29 15 :34 :15.159372 +0800 aegewgr[10664 :3638227 ] 自定义并发操作NSOperation 2017 -09 -29 15 :34 :15.159965 +0800 aegewgr[10664 :3638226 ] current Thread:<NSThread : 0x60400046b640 >{number = 4 , name = (null)}2017 -09 -29 15 :34 :15.160014 +0800 aegewgr[10664 :3638228 ] current Thread:<NSThread : 0x60000026d140 >{number = 5 , name = (null)}2017 -09 -29 15 :34 :15.160103 +0800 aegewgr[10664 :3638227 ] current Thread:<NSThread : 0x60400046b5c0 >{number = 3 , name = (null)}2017 -09 -29 15 :34 :15.160799 +0800 aegewgr[10664 :3638226 ] main end2017 -09 -29 15 :34 :15.160973 +0800 aegewgr[10664 :3638227 ] main end2017 -09 -29 15 :34 :15.161154 +0800 aegewgr[10664 :3638228 ] main end
NSOperationQueue NSOperationQueue就是执行NSOperation的队列,我们可以将一个或多个NSOperation对象放到队列中去执行。NSOpetaionQueue有两种不同类型的队列:主队列和自定义队列。主队列运行在主线程上,而自定义队列在后台执行。
1 2 3 4 5 6 7 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ }]; [queue addOperation:operation];
maxConcurrentOperationCount默认值 maxConcurrentOperationCount 最大并发操作数。用来控制一个特定队列中可以有多少个操作同时参与并发执行
maxConcurrentOperationCount 默认情况下为-1,表示不进行限制,可进行并发执行。 maxConcurrentOperationCount 为1时,队列为串行队列。只能串行执行 maxConcurrentOperationCount大于1时,队列为并发队列。操作并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整为 min{自己设定的值,系统设定的默认最大值}。
GCD与NSOperationQueue有哪些异同?
GCD 是纯 C 语言的 API 。NSOperationQueue 是基于 GCD 的 OC 的封装。
GCD 只支持 FIFO 队列,NSOperationQueue 可以方便设置执行顺序,设置最大的并发数量。
NSOperationQueue 可是方便的设置 operation 之间的依赖关系,GCD 则需要很多代码。
NSOperationQueue 支持 KVO,可以检测 operation 是否正在执行(isExecuted),是否结束(isFinished),是否取消(isCanceled)
NSOperationQueue可以很方便的取消一个NSOperation的执行。
使用场合:
任务之间不太相互依赖:GCD
任务之间有依赖或要监听任务的执行情况:NSOperationQueue
参考
iOS多线程详解