多线程涉及到的概念:
进程,线程,主线程,任务,队列,死锁,串行,并行,同步,异步,GCD,NSOperation,NSThread
iOS多线程的实现方式:
1. Pthreads
具体见:
https://www.jianshu.com/p/0b0d9b1f1f19
2. NSThread
(一)创建方法:
NSThread*thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(run:) object:nil];
创建并自动启动 [NSThread detachNewThreadSelector:@selector(run:) toTarget:selfwithObject:nil];
+ (void)detachNewThreadWithBlock:(void(^)(void))blockAPI_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullableid)argument;
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullableid)argAPI_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
等等
(二)线程休眠方法
+ (void)sleepUntilDate:(NSDate*)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
(三)强制退出线程
+ (void)exit;
(四)线程安全
多个线程访问同一个数据,加互斥锁
@synchronized (self)
{
//访问临界区
}
(五)线程通信
由子线程回归主线程
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullableid)arg waitUntilDone:(BOOL)wait modes:(nullableNSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullableid)arg waitUntilDone:(BOOL)wait;
//waitUntilDone YES aSelector执行完再去执行后续代码,NO 并发执行
- (void)performSelector:(SEL)aSelector onThread:(NSThread*)thr withObject:(nullableid)arg waitUntilDone:(BOOL)wait modes:(nullableNSArray *)arrayAPI_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (void)performSelector:(SEL)aSelector onThread:(NSThread*)thr withObject:(nullableid)arg waitUntilDone:(BOOL)waitAPI_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
敬请期待:
3. GCD
任务:执行什么操作
队列:用来存放任务
GCD使用:1.定制任务(确定想做的事情),2.将任务添加到队列(GCD会自动将队列的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则)
执行任务的方式:
同步任务,
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
异步任务,
dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
同步和异步任务的区别:
dispatch_sync和 dispatch_async需要两个参数,一个是队列,一个是block,它们的共同点是block都会在你指定的队列上执行(无论队列是并行队列还是串行队列),不同的是dispatch_sync会阻塞当前调用GCD的线程直到block结束,而dispatch_async异步继续执行。
同步,在当前线程中执行任务,不具备开启新的线程的能力。
异步,可以在block中开启新的线程。
并发队列和串行队列:
并发队列可以让多个任务并发(同时)执行,(可以自动开启多个线程同时执行任务),只能在dispatch_async中开启并发队列。
串行队列,让任务一个一个执行
并行队列的创建:
1. dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>)
label:队列名称
attr:队列属性,(DISPATCH_QUEUE_CONCURRENT )表示并行队列
2. dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>)
identifier:队列标识
flags:0
串行行队列的创建:
1. dispatch_queue_create(<#const char * _Nullable label#>,DISPATCH_QUEUE_SERIAL)
DISPATCH_QUEUE_SERIAL 或者NULL表示串行队列。
2. dispatch_get_main_queue()
主队列是 GCD自带的一种特殊串行队列,放在主队列中的任务都会在主线程中执行。
并发队列加上同步任务不会开启新的线程,任务逐个执行。
并发队列加上异步任务,会开启新的线程,任务并发执行。
全局队列中加同步任务,不会开启新的线程,任务逐个执行。
全局队列中加异步任务,开启新的线程,任务并发执行的。
串行队列加上异步任务会开启新的线程,任务逐个执行。
主队列(串行队列)(只有一个线程)加上同步任务,造成死锁。
主队列(串行队列)(只有一个线程)加上异步任务,不会创建新的线程。
注意区分全局队列和主队列。
线程通信
dispatch_asyn(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//耗时操作
//加载网络图片
//回到主线程
dispatch_syn(dispatch_get_main_queue(),^{
self.imageView = image;
})
//继续线程操作
})
4.NSOperation
https://www.jianshu.com/p/a41f1e69ce44
文章写的很详细。
NSOperation 只是对执行代码的封装,本身并不提供任何的异步功能,NSOperationQueue 底层是基于 GCD 的封装,所以一般来说,我们会用 NSOperationQueue 来控制 operation 的执行,queue 会根据 operation 的优先级、依赖等来决定如何执行添加进 queue 的 operation。
1. NSInvocationOperation
//创建NSInvocationOperation对象
NSInvocationOperation*invoOperation = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(doSomeThing) object:nil];
//调用start
方法来执行操作[invoOperation start];
注意⚠️:
默认情况下,调用start方法不会开启一条新的线程去执行操作,而是在当前线程同步执行操作。
只有将NSOperation放到NSOperationQueue中,才会异步执行操作。
2. NSBlockOperation
//创建NSBlockOperation对象
NSBlockOperation*blockOperation1 = [NSBlockOperationblock OperationWithBlock:^{NSLog(@"create1----%@",[NSThread currentThread]); }];
//通过addExecutionBlock:方法添加更多的操作
[blockOperation1 addExecutionBlock:^{NSLog(@"add1-----%@",[NSThreadcurrentThread]); }];
[blockOperation1 start];
[blockOperation2 start];
可以看出NSBlockOperation如果封装了多个操作,那么除了第一个操作外,其他的操作会在子线程中进行。如果只封装了一个操作,默认在主线程中进行,并且只要NSBlockOperation封装的操作数大于一个,就会异步执行操作。
3. 自定义NSOperation
自定义NSOperation可以通过重写 main 或者start方法 来定义自己的 operations 。
使用 main方法非常简单,开发者不需要管理一些状态属性(例如 isExecuting 和 isFinished),当 main 方法返回的时候,这个 operation 就结束了。这种方式使用起来非常简单,但是灵活性相对重写 start 来说要少一些, 因为main方法执行完就认为operation结束了,所以一般可以用来执行同步任务。
重写main方法有一个注意点:
需要自己创建自动释放池(如果是异步操作,无法访问主线程的自动释放池)
为了能使用操作队列所提供的取消功能,你需要在长时间操作中时不时地检查 isCancelled属性
NSOperation Queue
将NSOperation添加到NSOperationQueue中,系统会异步执行NSOperation的操作
将NSOperation添加到NSOperationQueue方法
1. addOperation
2.addOperationWithBlock
GCD队列:全局队列,主队列,自定义串行队列,自定义并行队列
NSOperation的队列:主队列,自己定义队列
NSOperation的线程通信
NSOperationQueue *queue =[ [NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
//获取网络图片(耗时操作)
//获取主线程
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
//主线程显示图片
slef.imageView.image = image;
}];
}];
NSOperation 任务依赖
//添加操作依赖
[blockOperation1 addDependency:blockOperation2];
[blockOperation3 addDependency:blockOperation1];
//添加操作到NSOperationQueue
[myQueue addOperation:blockOperation1];
[myQueue addOperation:blockOperation2];
[myQueue addOperation:blockOperation3];
先执行blockOperation2,再执行blockOperation1,最后执行blockOperation3。
NSOperation并发与取消,挂起
//NSOperationQueue最大并发数量
可以设置maxConcurrentOperationCount
//取消队列的所有操作
[myQueue cancelAllOperations];
//取消某一个操作
[blockOperation2 cancel];
[myQueue setSuspended:YES];
myQueue.suspended = YES;
最大并发操作数量:
队列中最多同时运行几条线程。虽然NSOperationQueue类设计用于并发执行Operations,你也可以强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因素,比如operation是否被取消(if(self.cancelled))和operation的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue.