iOS多线程

先了解一下进程和线程吧,面试也经常问到。

  • 进程:是系统进行资源分配和调度的一个独立单位,拥有独立的内存空间,
  • 线程:线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源。
  • 关系:一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
  • 区别:进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
    1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
  1. 线程的划分尺度小于进程,使得多线程程序的并发性高。
  2. 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
  3. 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  4. 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
  • 优缺点: 线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
1.iOS中的多线程比较

类型|特点
---|---|---
NSThread |生命周期需要自己管理,不够方便使用,一般用来获取当前线程使用。
NSOperation|父类,抽象类,不能直接使用 基GCD的封装,使用起来面向对象一些。生命周期不用自己管理。可以设置依赖关系控制线程先后执行的顺序
GCD|基于C语言,功能强大。在block回调中执行任务,使用方便。线程执行后不能取消,生命周期不用管理

2. NSThread
  • 类方法
 //新开线程,在block中执行任务
 [NSThread detachNewThreadWithBlock:^{
        NSLog(@"%@",[NSThread currentThread]);

    }];
 //新开线程,在@selector响应方法中执行任务
 [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

 -(void)run
 {
    NSLog(@"%@",[NSThread currentThread]);
   //退出当前线程   
   [NSThread exit];
 }
  • 对象方法
//返回创建的线程,在block中执行任务
NSThread *thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
//要start才能开始执行任务
 [thread start];

//新开线程,在@selector响应方法中执行任务
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
[self.thread start];

 -(void)newThread
 {
       NSLog(@"newThread%@",[NSThread currentThread]);
       //退出当前线程   
       [NSThread exit];
  }

  • 直接self调用
  [self performSelector:@selector(run) onThread:self.thread withObject:nil waitUntilDone:YES];
  [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];
2. NSOperation
  • NSOperation :父类,抽象类,不能直接使用。

  • NSOperationQueue : 队列

  • NSBlockOperation: 线程,在block回调里面处理任务

  • NSInvocationOperation : 线程,在@selector方法里面调用任务

  • NSOperationQueuePriority : 线程优先级

    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8

//创建block回调线程任务
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
       NSLog(@"blockOperation:%@",[NSThread currentThread]);
 }];
//创建在@selector方法里面调用线程任务
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];

//创建队列,然后将线程任务添加到队列中,自动执行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//设置队列的最大并发线程量。默认是NSOperationQueueDefaultMaxConcurrentOperationCount
queue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount;

//设置依赖关系,invocationOperation依赖blockOperation,即是invocationOperation在blockOperation执行完成之后再执行,如果不设置的话就是并发执行。
//[invocationOperation addDependency:blockOperation];
//添加多条线程到队列中
[queue addOperations:@[blockOperation,invocationOperation] waitUntilFinished:YES];

//单独添加一条线程
//    [queue addOperation:invocationOperation];
//    [queue addOperation: blockOperation];

-(void)invocationOperation
{
   //加线程锁,单一线程资源访问,不能同时多个线程访问。
    @synchronized (self) {
         NSLog(@"invocationOperation:%@",[NSThread currentThread]);
   }
}

3.GCD
类型 说明
同步(sync) 它会阻塞当前线程(即停止当前的任务)执行新任务执行完毕,然后当前任务才会继续往下运行,都是在当前线程执行,不会另开线程。同步线程中,使用串行队列和并行队列并无区别。
异步(async) 当前线程会直接往下执行,它不会阻塞当前线程。会另开线程,在别的线程执行。
1.如果使用的是串行队列,那么只会创建一条新的线程,然后在新的线程里面依次执行任务。
2.如果是使用的并行队列,那么在每次添加了任务之后都会创建一个新的线程去并行执行,全局并发队列是系统提供的一个并发队列。
异步线程组(group_async) 基础功能和异步(async)一样,但是加了一个group的概念,在group的队列执行完毕之后会有统一的回调通知dispatch_group_notify和dispatch_group_wait。
串行队列 1.如果是在同步线程中,那么不会开辟创建新线程,在当前线程执行队列中的任务。
2.如果是在异步线程中,则开启一条新线程,在新线程中执行队列中的任务。
并发队列 1.如果是在同步线程中,那么不会开辟创建新线程,在当前线程执行队列中的任务。(和串行队列没区别)。
2.如果是在异步线程中,则每一个队列每调用一次就会开启一条新线程,在新线程中并发执行队列中的任务。
全局并发队列 系统提供的一个并发队列。可直接调用,不用创建
3.1队列的创建
//@param <#const char * _Nullable label#>指的是队列的名称,传入C语言字符串
//<#dispatch_queue_attr_t  _Nullable attr#>指的是队列的类型
//DISPATCH_QUEUE_SERIAL and NULL  串行队列
//DISPATCH_QUEUE_CONCURRENT   并行队列

dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t  _Nullable attr#>)

串行队列( DISPATCH_QUEUE_SERIAL || NULL)

dispatch_queue_t serialQueue1 = dispatch_queue_create("syncQueue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("syncQueue2", NULL);

并行队列 (DISPATCH_QUEUE_CONCURRENT)

dispatch_queue_t concurrent1 = dispatch_queue_create("concurrent1", DISPATCH_QUEUE_CONCURRENT);

全局队列

  • 第一个参数是优先级
    • DISPATCH_QUEUE_PRIORITY_HIGH 高优先级
    • DISPATCH_QUEUE_PRIORITY_DEFAULT 默认优先级
    • DISPATCH_QUEUE_PRIORITY_LOW 低优先级
    • DISPATCH_QUEUE_PRIORITY_BACKGROUND 在background执行

+第二个参数 flag Reserved for future use 预留的参数,传0就可以了

dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

主队列

 dispatch_get_main_queue()
3.2同步线程 (sync)

注意同步线程操作传入的队列不能是当前队列,如果是当前队列会造成线程死锁的状态,下面会讲到

   //DISPATCH_QUEUE_SERIAL 串行队列
    dispatch_queue_t serialQueue1 = dispatch_queue_create("syncQueue1", DISPATCH_QUEUE_SERIAL);
   //创建同步线程,在队列block回调中添加执行任务
    dispatch_sync(serialQueue1, ^{
        //添加执行的任务
        NSLog(@"serialQueue1-->%@",[NSThread currentThread]);
    });
3.3异步线程 (async)

**异步线程串行队列和异步线程并发队列的差别主要是: **
**只有一条队列的情况下,如果是串行队列那么只会开启一条新的线程,然后任务依次执行。如果是并发队列的话,那么每添加一次任务都会开启一条新的线程,然后个人线程并发执行
**

  • 异步线程串行队列
    //DISPATCH_QUEUE_SERIAL 串行队列
    dispatch_queue_t serialQueue1 = dispatch_queue_create("syncQueue1", DISPATCH_QUEUE_SERIAL);
    //创建异步线程,开启一条新线程执行任务
    dispatch_async(serialQueue1, ^{
        //串行队列执行
        NSLog(@"serialQueue1-->%@",[NSThread currentThread]);
        for (int i = 10; i<20; i++) {
            NSLog(@"%d",i);
        }
    });


  • 异步线程并发队列
    //DISPATCH_QUEUE_CONCURRENT 并行队列
    dispatch_queue_t concurrent1 = dispatch_queue_create("concurrent1", DISPATCH_QUEUE_CONCURRENT);
    //创建异步线程,在队列block回调中添加执行任务
    dispatch_async(concurrent1, ^{
        //并行队列执行
        NSLog(@"global1-->%@",[NSThread currentThread]);
        for (int i = 40; i<50; i++) {
            NSLog(@"%d",i);
        }
    });

     //全局队列 flag Reserved for future use 预留的参数,传0就可以了
      dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     //创建异步线程,在队列block回调中添加执行任务
      dispatch_async(global, ^{
        NSLog(@"global1-->%@",[NSThread currentThread]);
        for (int i = 40; i<50; i++) {
            NSLog(@"%d",i);
        }
    });

  • 线程卡死问题
    在原来的线程上添加一个同步串行任务,如果添加的串行队列就是当前队列的话就会造成线程卡死,因为原本线程是在该队列中执行的,这里又创建了一个任务在该队列中,原先的任务就会停止,等待这个队列的新任务执行完毕后再执行主队列的任务,但是这里创建的队列又是原先队列,任务是添加该队列中,同一线程中相互等待,造成线程死锁状态。
  //用这个是会堵塞死线程,因为原本线程是在主队列中执行的,这里又创建了一个队列主队列的任务就会停止,等待这个队列的任务执行完毕后再执行主队列的任务,但是这里创建的队列又是主队列,任务是添加在主队列中,所有main线程中相互等待,造成线程死锁状态
 //主线程
-(void)test
{
    NSLog(@"我是主线程");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"在这里我会被卡死,我不会被log出来");
    });
    NSLog(@"上面已经卡死了,所以我也不会被log出来");
}


3.4Dispatch Groups的使用
  • dispatch_group_async 异步队列组。也是一种创建异步线程执行的方法,和dispatch_async一样
  • dispatch_group主要是来进行线程同步,例如我们通常一个页面中有多个网络请求,然后我们需要管理他们的先后请求顺序(当然这里我们可以使用异步串行也可以实现),使用dispatch_group的好处是group中的任务都执行完毕以后会有一个通知回调,我们可以在通知回调里面做我们的操作。例如刷新UI页面,不过这个回调是异步线程里面的,需要回到主线程刷新。
  • dispatch_group_notify group中所有任务都执行完毕有的回到
线程组串行队列
    //DISPATCH_QUEUE_SERIAL 串行队列
    dispatch_queue_t serialQueue1 = dispatch_queue_create("syncQueue1", DISPATCH_QUEUE_SERIAL);
   //创建group
    dispatch_group_t group = dispatch_group_create();
   //添加队列到group中,执行任务
    dispatch_group_async(group, serialQueue1, ^{
        NSLog(@"你大爷-->%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, serialQueue1, ^{
        NSLog(@"你二大爷-->%@",[NSThread currentThread]);
    });
    //group中的任务都执行完毕了的回调,注意这里的回调不是主线程
    dispatch_group_notify(group, concurrent1, ^{
    //在这里面可以做刷新UI页面等操作,不过要回到主线程中
         NSLog(@"你大爷和你二大爷都执行完毕了-->%@",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
        NSLog(@"dispatch_group_wait 结束");
    });


看看执行这段代码打印的,这里只开启了一条线程串行执行任务,执行完毕后有通知回调


WechatIMG5.jpeg
线程组并行队列
    //DISPATCH_QUEUE_CONCURRENT 并行队列
    dispatch_queue_t concurrent1 = dispatch_queue_create("concurrent1", DISPATCH_QUEUE_CONCURRENT);

   //创建group
    dispatch_group_t group = dispatch_group_create();
   //添加队列到group中,执行任务
    dispatch_group_async(group, concurrent1, ^{
        for (int i = 0; i<100; i++) {
            NSLog(@"你大爷-->%@",[NSThread currentThread]);
        }
    });
    dispatch_group_async(group, concurrent1, ^{
         for (int i = 0; i<100; i++) {
            NSLog(@"你二大爷-->%@",[NSThread currentThread]);
          }   
     });
    //group中的任务都执行完毕了的回调,注意这里的回调不是主线程
    dispatch_group_notify(group, concurrent1, ^{
    //在这里面可以做刷新UI页面等操作,不过要回到主线程中
         NSLog(@"你大爷和你二大爷都执行完毕了-->%@",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
        NSLog(@"dispatch_group_wait 结束");
    });

log中可以看出这里面开启了两条线程异步执行任务,执行完毕后有通知回调(log太多没有截图完整)。


iOS多线程_第1张图片
WechatIMG6.jpeg
4.GCD的一些其他的使用
单例
 static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
    });
延迟执行
 //<#delayInSeconds#>延迟时间
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
    });
线程挂起和重启
  • dispatch_suspend 挂起 **在创建线程前挂起才有效,当创建的线程已经在执行了就无效了,即GCD无法终止执行中的线程 **
  • dispatch_resume 开启
    dispatch_queue_t serialQueue = dispatch_queue_create("syncQueue1", DISPATCH_QUEUE_SERIAL);
 //挂起线程,此时下面的线程不会开启
    dispatch_suspend(serialQueue);
    dispatch_async(serialQueue, ^{
        //并行队列执行
        for (int i = 40; i<50; i++) {
            NSLog(@"嘻嘻嘻嘻-->%@",[NSThread currentThread]);
        }
    });
    //休眠4秒后重启线程,这时候才会执行block里面代码块
    sleep(4.0);
    dispatch_resume(serialQueue2);
dispatch_source_t 实现一个简单的计时器
 //倒计时时间
    __block int timeout= 5;
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, concurrent1);
  //这里面四个参数
  //1.dispatch_source_t source 传入上面的source
  //2.dispatch_time_t start 开始时间
  //3.uint64_t interval  间隔时间
  //4.uint64_t leeway 落后时间(设置了貌似没什么效果)
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC,0);
    dispatch_source_set_event_handler(timer, ^{
        if (timeout > 0) {
            //在这里执行轮询
            NSLog(@"循环中");
            timeout--;
        }else{
            //关闭计时器
            dispatch_source_cancel(timer);
        }
    });
    //开启
    dispatch_resume(timer);

你可能感兴趣的:(iOS多线程)