美文网首页
iOS多线程

iOS多线程

作者: yuandiLiao | 来源:发表于2017-05-23 11:49 被阅读0次

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

    • 进程:是系统进行资源分配和调度的一个独立单位,拥有独立的内存空间,
    • 线程:线程是进程的一个实体,是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太多没有截图完整)。


    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多线程

          本文链接:https://www.haomeiwen.com/subject/xpewxxtx.html