一、NSThread算是多线程里面最麻烦的一种,什么都要自己来,需要自己管理线程的生命周期,线程同步,加锁。。。
1.1创建线程一共有四种方式
方法一:
NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(loadImage) object:nil]; //调用start方法启动线程,执行线程里的main方法 //而这里的start只是准备就绪,并不是代表马上就执行,还是需要等到系统调度时才执行 [thread start];
方法二:
[NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil];
方法三:
//通过NSThreadPerformAdditions创建一个新的线程 [self performSelectorInBackground:@selector(loadImage) withObject:nil];
方法四:
//自定义线程,类继承于NSThread,重写main方法 CustomThread * cusThread = [[CustomThread alloc]init]; [cusThread start]; //直接把需要执行的任务写到自己创建的类的main函数里。
看看NSThread里面有什么
+ (NSThread *)currentThread;//当前获取到的线程是哪个 + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument; //前面讲到的创建线程的第二张方法 + (BOOL)isMultiThreaded;//是不是多线程 @property (readonly, retain) NSMutableDictionary *threadDictionary;//线程对象字典 + (void)sleepUntilDate:(NSDate *)date;//表示线程处于休眠状态,虽然睡眠了线程还是没有放弃对CPU的使用权,等时间过了以后又会复苏。 + (void)sleepForTimeInterval:(NSTimeInterval)ti; + (void)exit;//退出线程 + (double)threadPriority;//线程优先级,默认为0.5,范围是0-1,越大越优先 + (BOOL)setThreadPriority:(double)p;//可以设置线程 @property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);//线程名 @property NSUInteger stackSize NS_AVAILABLE(10_5, 2_0);//给线程分配的堆栈大小 @property (readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0);//是不是主线程 + (NSThread *)mainThread NS_AVAILABLE(10_5, 2_0);//获取到主线程,可以进行线程间通信 @property (readonly, getter=isExecuting) BOOL executing NS_AVAILABLE(10_5, 2_0);//线程处于使用状态 @property (readonly, getter=isFinished) BOOL finished NS_AVAILABLE(10_5, 2_0);//线程属于结束状态 @property (readonly, getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5, 2_0);//判断线程是不是将要结束 - (void)cancel NS_AVAILABLE(10_5, 2_0);//取消线程 - (void)start NS_AVAILABLE(10_5, 2_0);//开始线程 - (void)main NS_AVAILABLE(10_5, 2_0);//线程的入口方法 //在主线程中执行 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array; - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait; // equivalent to the first method with kCFRunLoopCommonModes //在某一线程中执行 - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array NS_AVAILABLE(10_5, 2_0); - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0); // equivalent to the first method with kCFRunLoopCommonModes //后台线程 - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
二、Cocoa operation不需要管理线程,数据同步,主要用到两个类NSOperation,NSOperationQueue(任务队列)。使用NSOperation创建一个任务,然后把任务添加到任务队列当中。然后又任务队列管理任务,把任务分配当相应的线程中执行。
2.1NSOperation(操作任务)是一个抽象类不能直接创建NSOperation的对象,使用它必须要用他的子类,可以使用系统自带的NSInvocationOperation或NSBlockOperation或者自定义一个NSOperation的子类实现内部相应的方法。
2.2NSOperationQueue(操作队列)用来管理operation,有两种Queue(队列)
主队列:添加到主队列中的操作都是在主线程上执行的,串行执行(就是排队一个一个轮过来)
NSOperationQueue * operationQueue = [NSOperationQueue mainQueue];
并发队列:任务默认并发执行(大家一起上),并发不一定并行多核才行。
//并发队列,通过NSOperation alloc创建的就是一个并发队列 NSOperationQueue * operationQueue = [[NSOperationQueue alloc]init];
2.3看看NSOperation有什么吧
//应有尽有啊,很多NSThread类似的 - (void)start; - (void)main; @property (readonly, getter=isCancelled) BOOL cancelled; - (void)cancel; @property (readonly, getter=isExecuting) BOOL executing; @property (readonly, getter=isFinished) BOOL finished; @property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below @property (readonly, getter=isAsynchronous) BOOL asynchronous NS_AVAILABLE(10_8, 7_0); @property (readonly, getter=isReady) BOOL ready; - (void)addDependency:(NSOperation *)op;//添加依赖 - (void)removeDependency:(NSOperation *)op;//移除依赖 //所谓依赖比如A依赖B, 那就好轮到A完成了才轮到B @property (readonly, copy) NSArray<NSOperation *> *dependencies;//添加依赖组 typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {//优先级 NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8 }; @property NSOperationQueuePriority queuePriority; @property (nullable, copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0);//完成这个线程后回调这个block - (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0); @property double threadPriority NS_DEPRECATED(10_6, 10_10, 4_0, 8_0); @property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0); @property (nullable, copy) NSString *name NS_AVAILABLE(10_10, 8_0); @end NS_CLASS_AVAILABLE(10_6, 4_0) @interface NSBlockOperation : NSOperation { @private id _private2; void *_reserved2; } + (instancetype)blockOperationWithBlock:(void (^)(void))block; - (void)addExecutionBlock:(void (^)(void))block; @property (readonly, copy) NSArray<void (^)(void)> *executionBlocks; - (void)addOperation:(NSOperation *)op;//添加任务 - (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);//添加多个任务 - (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);//写在block里可以理解为添加了一个任务,就不需要在创建一个任务对象在添加到任务队列里了。 @property (readonly, copy) NSArray<__kindof NSOperation *> *operations; @property (readonly) NSUInteger operationCount NS_AVAILABLE(10_6, 4_0);//队列中有几个任务 @property NSInteger maxConcurrentOperationCount;//最大的并发数 @property (getter=isSuspended) BOOL suspended;//悬停 @property (nullable, copy) NSString *name NS_AVAILABLE(10_6, 4_0); @property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0); @property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue NS_AVAILABLE(10_10, 8_0); - (void)cancelAllOperations;//取消队列中所以任务,并不是真正的取消只是把里面的cancel设为了yes - (void)waitUntilAllOperationsAreFinished; + (nullable NSOperationQueue *)currentQueue NS_AVAILABLE(10_6, 4_0); + (NSOperationQueue *)mainQueue NS_AVAILABLE(10_6, 4_0);
2.4向队列中添加任务
NSOperationQueue * operationQueue = [[NSOperationQueue alloc]init];//这样创建的队列就是一个并发队列。 //方法一 [operationQueue addOperationWithBlock:^{ NSLog(@"开启一个新任务"); }]; //方法二 NSBlockOperation * blockOperation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"再添加一个新任务"); }]; //将任务添加到任务队列 [operationQueue addOperation:blockOperation]; //方法三 NSInvocationOperation * invccationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(loadImage) object:nil]; //将任务加到队列 [operationQueue addOperation:invccationOperation]; //方法四 自定义一个和上面类似,类继承于NSOperation //更新UI的时候要在主线程,要先获取到主队列,按照上面的几种方法都可以 [[NSOperationQueue mainQueue] addOperation:blockOperation];//将任务添加到主队列 [[NSOperationQueue mianQueue] addOperationWithBlcok:^{ [self loadImage]; }] /* 假如把上面三种方法都写在一起,因为这是一个并发队列,所以就会开启三个子线程 */
2.5说一下优先级的小特点吧
-(void)viewDidLoad { NSOperationQueue * operationQueue = [[NSOperationQueue alloc]init]; [operationQueue addOperationWithBlock:^{ NSLog(@"开启一个新任务"); }]; NSBlockOperation * blockOperation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"再添加一个新任务"); }]; //将任务添加到任务队列 [operationQueue addOperation:blockOperation]; NSInvocationOperation * invccationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(loadImage) object:nil]; //将任务加到队列 [operationQueue addOperation:invccationOperation]; invccationOperation.queuePriority = NSOperationQueuePriorityHigh; } - (void)loadImage { NSLog(@"加载图片"); }
按照我们一开始的理解已经把加载图片这个任务优先级设置为最高了,应该打印出来的话加载图片应该是第一个,而事实的结果是
开启一个新任务
再添加一个新任务
加载图片
这是为什么?
对任务设置了优先级,然而只能改变未进行的已经进行的你是拦不住的。
三、GCD是一个C语言的多线程的实现方式。
GCD用起来超级方便,代码也很简单,
3.1同步,异步
//异步添加到主队线程,更新UI都要在主线程上进行 dispatch_async(dispatch_get_main_queue(), ^{ [self loadImage]; }); //同步添加到主线程 dispatch_sync(dispatch_get_main_queue(), ^{ [self loadImage]; }); //异步添加到全局并发队列 /* #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高 #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中) #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低 #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台 */ //dispatch_async(dispatch_get_global_queue(/*优先级*/, /*标记参数*/) dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self loadImage]; });
3.2GCD创建队列
//GCD创建串型队列 dispatch_queue_t serialQueue = dispatch_queue_create("serial"/*队列名*/,DISPATCH_QUEUE_SERIAL/*队列类型*/); dispatch_async(serialQueue, ^{ [self loadImage]; }); //GCD创建并发队列 dispatch_queue_t global = dispatch_queue_create("global", DISPATCH_QUEUE_CONCURRENT); dispatch_async(global, ^{ [self loadImage]; }); /* 一般不会去创建并发队列,都是直接拿全局并发队列来用 */
GCD循环等待的问题
- (vodi)viewDidLoad{ NSLog(@"1"); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2"); }); NSLog(@"3"); } /* 像这种情况会有什么问题呢? 运行后会发现只能打印出一个1 其他都打不出来了。 因为NSlog(@"2");是在主线程同步添加的,这样只有当NSLog(@"1")和NSLog(@"2")都打印完了才能运行主线程,而主线程是同步的,也只有当主线程上的NSLog(@"2");打印完了才能打印NSLog(@"3"); 这样永远无法完成,两个都会互相等待,导致一直卡在这里。这就是循环等待 可以吧同步改为异步,或者是吧主线程改成全局并发队列,就可以了。 */
整个结构可以这样理解 把进程看成是一个工厂