1.iOS多线程总结

   在iOS开发过程中,会遇到耗时操作或者多任务处理,为了能保证应用使用过程中的流畅性和发挥出多核优势,这个时候就会使用多线程。多线程可以发挥出多核的优势,如果线程数非常多,CPU会在N个线程之间切换,消耗大量的cpu资源。

    进程是通常是系统中正在运行的一个应用程序。

    线程是进程的基本执行单元,一个进程至少一个线程即主线程,所有的任务都在线程中执行。

    一个程序运行后,默认会开启1个线程,称为“主线程”或“UI线程”

   主线程一般用来  刷新UI界面 ,处理UI事件(比如:点击、滚动、拖拽等事件)

   主线程使用注意

   别将耗时的操作放到主线程中,耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种卡的坏体验。



2.iOS多线程的优缺点


开启多线程的优点

  • 能适当提高程序的执行效率
  • 能适当提高资源的利用率(cpu,内存)
  • 线程上的任务执行完成后,线程会自动销毁

开启多线程的缺点

  • 开启线程需要占用一定的内存空间(默认情况下,每一个线程都占512KB)
  • 如果开启大量的线程,会占用大量的内存空间,降低程序的性能
  • 线程越多,cpu在调用线程上的开销就越大
  • 程序设计更加复杂,比如线程间的通信、多线程的数据共享

3.常见的多线程方案



线程的状态



ios多线程按顺序执行 ios多线程的使用场景_主线程




线程调度池




ios多线程按顺序执行 ios多线程的使用场景_主线程_02



  常见的多线程方案:pthread,NSThread,GCD和NSOperation。

    

ios多线程按顺序执行 ios多线程的使用场景_多线程_03


 3.1 pthread的使用


引入头文件#import <pthread.h>
    //创建子线程
    //第一个参数线程编号的地址
    //第二个参数线程的属性
    //第三个参数 线程要执行的函数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;
}

 

NSThread的使用


创建线程的方式
+ (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<NSString *> *)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<NSString *> *)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;


3.3 GCD的使用


 

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);


各种队列的执行效果


ios多线程按顺序执行 ios多线程的使用场景_ios多线程按顺序执行_04


    

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
要执行的任务


GCD的一次执行

//当前线程上执行
//一次性执行的原理 ,判断静态的全局变量的值 默认是0,如果执行完成后,设置为-1
//once内部会判断变量的值,如果是0才执行
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    
});



GCD的调度组


//演示调度组的基本使用
- (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(), ^{
       
    });
}


3.4 NSOperation的使用

  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知道所有的操作都执行完


NSOperationQueue的 取消、暂停、恢复



取消队列的所有操作

-(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];



4 常见的同步锁

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 <libkern/OSAtomic.h>
-(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];
    });
    
}