在iOS开发过程中,会遇到耗时操作或者多任务处理,为了能保证应用使用过程中的流畅性和发挥出多核优势,这个时候就会使用多线程。多线程可以发挥出多核的优势,如果线程数非常多,CPU会在N个线程之间切换,消耗大量的cpu资源。
进程是通常是系统中正在运行的一个应用程序。
线程是进程的基本执行单元,一个进程至少一个线程即主线程,所有的任务都在线程中执行。
一个程序运行后,默认会开启1个线程,称为“主线程”或“UI线程”
主线程一般用来 刷新UI界面 ,处理UI事件(比如:点击、滚动、拖拽等事件)
别将耗时的操作放到主线程中,耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种卡的坏体验。
引入头文件#import
//创建子线程
//第一个参数线程编号的地址
//第二个参数线程的属性
//第三个参数 线程要执行的函数void * (*) (void *)
// int * 指向int类型的指针 void *指向任何类型的指针 有点类似OC中的id
//第四个参数要执行的函数的参数
pthread_t pthread; //线程编号
int result = pthread_create(&pthread, NULL, demo, NULL);
if (result == 0) {
NSLog(@"创建线程成功");
}else {
NSLog(@"创建线程失败");
}
void *demo(void *param) {
NSLog(@"hello %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:100000];
return NULL;
}
创建线程的方式
+ (void)detachNewThreadWithBlock:(void(^)(void))block;
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
- (instancetype)initWithBlock:(void(^)(void))block;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;
线程的休眠和退出
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
+ (void)exit;
GCD的队列可以分为2大类型
并发队列(Concurrent Dispatch Queue)
可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
并发功能只有在异步(dispatch_async)函数下才有效
串行队列(Serial Dispatch Queue)
让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
GCD同步执行和异步执行
GCD中有2个用来执行任务的函数
同步的方式执行任务
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue:队列
block:任务
异步的方式执行任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
各种队列的执行效果
Barrier阻塞
dispatch_queue_t queue = dispatch_queue_create("hm", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
//做一些耗时操作
//等待队列中所有的任务执行完成,才会执行barrier中的代码
dispatch_barrier_async(queue, ^{
//阻塞到耗时操作完成之后 执行里边的代码
});
});
GCD的延迟操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
dispatch_after的定义
dispatch_after(dispatch_time_t when,
dispatch_queue_t queue,
dispatch_block_t block);
dispatch_after的参数
参数1 dispatch_time_t when
多少纳秒之后执行
参数2 dispatch_queue_t queue
任务添加到那个队列
参数3 dispatch_block_t block
要执行的任务
//当前线程上执行
//一次性执行的原理 ,判断静态的全局变量的值 默认是0,如果执行完成后,设置为-1
//once内部会判断变量的值,如果是0才执行
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
//演示调度组的基本使用
- (void)diaoDuZu{
//创建组
dispatch_group_t group = dispatch_group_create();
//队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
});
dispatch_group_async(group, queue, ^{
});
dispatch_group_async(group, queue, ^{
});
//当三个异步任务都执行完成,才执行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
});
}
NSOperation和NSOperationQueue要配合使用
先将需要执行的操作封装到一个NSOperation对象中,然后将NSOperation对象添加到NSOperationQueue中,系统会自动将NSOperationQueue中的NSOperation取出来,将取出的NSOperation封装的操作放到一条新线程中执行。
NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
使用NSOperation子类的方式有3种:
NSInvocationOperation
NSBlockOperation
自定义子类继承NSOperation,实现内部相应的方法。
NSInvocationOperation类的使用
创建NSInvocationOperation对象
-(id)initWithTarget:(id)targetselector:(SEL)selobject:(id)arg;
调用start方法开始执行操作
-(void)start;
一旦执行操作,就会调用target的sel方法
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
[op start];
- (void)run
{
NSLog(@"------%@", [NSThread currentThread]);
}
默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
NSBlockOperation类的使用
创建NSBlockOperation对象
+(id)blockOperationWithBlock:(void(^)(void))block;
通过addExecutionBlock:方法添加更多的操作
-(void)addExecutionBlock:(void(^)(void))block;
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主线程
NSLog(@"下载1------%@", [NSThread currentThread]);
}];
// 添加额外的任务(在子线程执行)
[op addExecutionBlock:^{
NSLog(@"下载2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载4------%@", [NSThread currentThread]);
}];
[op start];
注意:只要NSBlockOperation封装的操作数 >1,就会异步执行操作
NSOperationQueue的使用
NSOperation可以调用start方法来执行任务,但默认是同步执行的
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
添加操作到NSOperationQueue中
-(void)addOperation:(NSOperation*)op;
-(void)addOperationWithBlock:(void(^)(void))block;
方法一:
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 创建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
//添加操作到队列中
[queue addOperation:op1];
方法二:
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//添加操作到队列中
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
NSOperationQueue的最大并发数
-(NSInteger)maxConcurrentOperationCount;
-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 设置最大并发操作数
// queue.maxConcurrentOperationCount = 2;// 并发队列
queue.maxConcurrentOperationCount = 1; // 就变成了串行队列
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread])
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread])
}];
[queue addOperationWithBlock:^{
NSLog(@"download3 --- %@", [NSThread currentThread])
}];
1、把操作添加到队列self.queue addOperationWithBlock
2、去线程池去取空闲的线程,如果没有就创建线程
3、把操作交给从线程池中取出的线程执行
4、执行完成后,把线程再放回线程池中
5、重复2,3,4知道所有的操作都执行完
取消队列的所有操作
-(void)cancelAllOperations;
提示:也可以调用NSOperation的-(void)cancel方法取消单个操作
暂停和恢复队列
-(void)setSuspended:(BOOL)b;// YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended
代码
// 恢复队列,继续执行
// self.queue.suspended = NO;
// 暂停(挂起)队列,暂停执行
// self.queue.suspended = YES;
// 取消队列的所有操作
[self.queue cancelAllOperations];
NSOperation的操作依赖
NSOperation之间可以设置依赖来保证执行顺序
比如一定要让操作A执行完后,才能执行操作B,可以这么写
[operationB addDependency:operationA];// 操作B依赖于操作A
也可以在不同queue的NSOperation之间创建依赖关系
代码
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 添加操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1];
NSLog(@"download1----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1];
NSLog(@"download2----%@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1];
NSLog(@"download3----%@", [NSThread currentThread]);
}];
// 设置依赖(保证op3在op1和op2都执行完之后再执行)
[op2 addDependency:op1];
[op3 addDependency:op2];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
recursiveLock=[[NSRecursiveLock alloc] init];
condition= [NSCondition new];
conditionLock=[NSConditionLock new];
#pragma mark synchronized
//性能较差 加锁的代码尽量少 添加的OC对象必须在多线程中都是同一对象
-(void)lock_synchronized{
@synchronized(self){
}
}
#pragma mark 互斥锁
-(void)lock_mutexLock{
[mutexLock lock];
//添加逻辑代码
[mutexLock unlock];
}
#pragma mark 自旋锁
//需要引入头文件 #import
-(void)lock_OSSpinLockLock{
// 初始化
OSSpinLock spinLock = OS_SPINLOCK_INIT;
// 加锁
OSSpinLockLock(&spinLock);
// 解锁
OSSpinLockUnlock(&spinLock);
}
#pragma mark 递归锁
-(void)lock_NSRecursiveLock:(int )count{
[recursiveLock lock];
if (count!=0) {
count--;
[self lock_NSRecursiveLock:count];
}
[recursiveLock unlock];
}
#pragma mark 条件锁NSCondition
//wait:进入等待状态
//waitUntilDate::让一个线程等待一定的时间
//signal:唤醒一个等待的线程
//broadcast:唤醒所有等待的线程
-(void)lock_condition{
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[condition lock];
NSLog(@"线程1加锁成功");
[condition wait];
NSLog(@"线程1");
[condition unlock];
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[condition lock];
NSLog(@"线程2加锁成功");
[condition wait];
NSLog(@"线程2");
[condition unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
NSLog(@"唤醒一个等待的线程");
[condition signal];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
NSLog(@"唤醒所有等待的线程");
[condition broadcast];
});
}
//- (instancetype)initWithCondition:(NSInteger)conditionNS_DESIGNATED_INITIALIZER;
//@property (readonly)NSInteger condition; //这属性非常重要,外部传入的condition与之相同才会获取到lock对象,反之阻塞当前线程,直到condition相同
//- (void)lockWhenCondition:(NSInteger)condition; //condition与内部相同才会获取锁对象并立即返回,否则阻塞线程直到condition相同
//- (BOOL)tryLock;//尝试获取锁对象,获取成功需要配对unlock
//- (BOOL)tryLockWhenCondition:(NSInteger)condition; //同上
//- (void)unlockWithCondition:(NSInteger)condition; //解锁,并且设置lock.condition = condition
//- (BOOL)lockBeforeDate:(NSDate *)limit;
//- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
-(void)lock_conditionLock{
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[conditionLock lock];
[conditionLock unlockWithCondition:1];
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[conditionLock lockWhenCondition:1];
[conditionLock unlock];
});
}