美文网首页
iOS - 多线程三部曲之GCD(二)

iOS - 多线程三部曲之GCD(二)

作者: flowerflower | 来源:发表于2018-10-09 23:11 被阅读152次
面朝大海 春暖花开

对于太多数人来说,可能都听过,也有可能在项目中用到,对于初学者来说,似乎可能没多大的用户,于是在同步、异步、串行、并行和死锁当中渐渐滴放弃治疗,接下来本文将会彻底长谈GCD。

目录
一、基础理论
1.1 GCD概念
1.2 线程、队列、任务概念
1.3 同步、异步、并发、串行之间的差异
1.4 GCD使用目的
1.5 GCD的优势
1.6 GCD的使用步骤
二、队列任务组合方式
2.1 组合方式
2.2 demo演示
三、结合案例使用
案例1:子线程下载图片->主线程显示图片
案例2:子线程同时执行ABC三个同步任务、全部执行完成再在子线程执行三个同步任务DEF
案例3:售票的小故事(窗口1和窗口2同时售票,售完即止)

一、基础理论

1.1 GCD概念

GCD(Grand Central Dispatch)是基于C的底层的API,完全面向过程,使用简单。

1.2 线程、队列、任务概念
线程、队列、任务

队列遵循先进性先出原则(FIFO),GCD提供的队列有:并发队列、串行队列、全局队列、主队列。

1.3 同步、异步、并发、串行之间的差异
  • 同步和异步的区别:能不能开启新的线程
  • 并发和串行的区别:任务的执行方式
同步、异步、并发、串行
1.4 GCD使用目的:

GCD最大的目的就是在新的线程中同时执行多个任务,则需满足2个条件:

  • 能开启新的线程
  • 任务可以同时执行
1.5 GCD的优势

GCD是苹果公司为多核的并行运行提出的解决方案,提供了直接并且简单的调用接口,使用方便
GCD会自动利用更多的CPU内核,更快的内存效率,因为线程栈不暂存于应用内存。
GCD会自动管路线程的生命周期(包括创建线程、调度任务、销毁线程),提供了自动的和全面的线程池管理机制,稳定而便捷
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理的相关代码

1.6 GCD的使用步骤
  • 1 定制任务(确定想做的事情)
  • 2 将任务添加到队列中
    GCD会自动将队列中的任务取出,放到对应的线程中
    任务的取出遵循队列的FIFO(先进先出,后进后出)

二、队列任务组合方式

2.1 组合方式
  • 并发队列 + 异步执行 = 多个任务同时执行 + 子线程执行

  • 串行队列 + 异步执行 = 多个任务串行执行 + 子线程执行

  • 全局队列 + 异步执行 = 多个任务同时执行 + 子线程执行

  • 主队列 + 异步执行 = 多个任务串行执行 + 主线程执行

  • 并发队列 + 同步执行 = 多个任务串行执行 + 主线程执行

  • 串行队列 + 同步执行 = 多个任务串行执行 + 主线程执行

  • 主队列 + 同步执行 = 死锁(两个任务处于相互等待状态)

2.2 demo演示
  • 并发队列 + 异步执行 = 多个任务同时执行 + 子线程执行 (使用频率较高)
//  并发列队+ 异步执行 :队列中的任务是并发执行,会开启多条线程
    /**
     DISPATCH_QUEUE_CONCURRENT:并发 0表示并发
     DISPATCH_QUEUE_SERIAL:串行
     */
    //创建并发队列(指定队列名字+队列类型)
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(@"-------%@",[NSThread currentThread]);

    dispatch_async(queue, ^{

        NSLog(@"任务1----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3----%@",[NSThread currentThread]);
    });
    
并发队列 + 异步执行.png
  • 串行队列 + 异步执行 = 多个任务串行执行 + 子线程执行
//串行队列 + 异步执行:队列中的任务是串行执行的,会开线程
  //串行:按顺序执行任务
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
    
    NSLog(@"-------%@",[NSThread currentThread]);
    
    dispatch_async(queue, ^{
        
        NSLog(@"任务1----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3----%@",[NSThread currentThread]);
    });
串行队列 + 异步执行.png
  • 全局队列 + 异步执行 = 多个任务同时执行 + 子线程执行
  //获取全局队列
    dispatch_queue_t queue =  dispatch_get_global_queue(0, 0);
        NSLog(@"---satrt----");
        dispatch_async(queue, ^{
            NSLog(@"任务1----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"任务2----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"任务3----%@",[NSThread currentThread]);
        });
        NSLog(@"---end---");
全局队列 + 异步执行.png
  • 主队列 + 异步执行 = 多个任务串行执行 + 主线程执行
//主队列 + 异步执行:所有任务都在主线程中执行,不会开线程
    //(特殊串行队列/主队列)
    //获取主队列
    dispatch_queue_t queue =  dispatch_get_main_queue();
    NSLog(@"---satrt------%@",[NSThread currentThread]);
    dispatch_async(queue, ^{
        NSLog(@"任务1----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3----%@",[NSThread currentThread]);
    });
    NSLog(@"---end---");
主队列 + 异步执行.png
  • 并发队列 + 同步执行 = 多个任务串行执行 + 主线程执行
//并发队列 + 同步执行 : 任务是串行执行的,不会开线程
    dispatch_queue_t queue  = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"---satrt------%@",[NSThread currentThread]);
    dispatch_sync(queue, ^{
        NSLog(@"任务1----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务2----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务3----%@",[NSThread currentThread]);
    });
    NSLog(@"end---");
并发队列 + 同步执行.png
  • 串行队列 + 同步执行 = 多个任务串行执行 + 主线程执行
//串行队列 + 同步执行:任务是串行执行的,不会开线程

 dispatch_queue_t queue  = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
    NSLog(@"---satrt------%@",[NSThread currentThread]);
    dispatch_sync(queue, ^{
        NSLog(@"任务1----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务2----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务3----%@",[NSThread currentThread]);
    });
    NSLog(@"end---");

串行队列 + 同步执行.png
  • 主队列 + 同步执行 = 死锁(两个任务处于相互等待状态)
主队列 + 同步执行.png

三、结合案例使用

案例1:针对NSThread中的案例一(子线程下载图片->主线程显示图片)采取GCD方式实现

- (IBAction)demo6 {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"--%@",[NSThread currentThread]);//--<NSThread: 0x60400027b740>{number = 3, name = (null)}
        NSURL *url = [NSURL URLWithString:@"https://img.haomeiwen.com/i1658521/929b88123cf7156c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"];
        UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
        
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"-main-%@",[NSThread currentThread]); //-main-<NSThread: 0x60000006cc00>{number = 1, name = main}
            weakSelf.imgView.image = image;
        });
    });  
}

案例2: 子线程同时执行ABC三个同步任务、全部执行完成再在子线程执行三个同步任务DEF

 /**
     1.创建并发队列(指定定队列名字+队列类型)
       队列类型:
       DISPATCH_QUEUE_CONCURRENT(并发队列): 不等待现在执行中的处理是否结束,继续执行下面的处理。只有在异步执行中,才能体现并发性
       DISPATCH_QUEUE_SERIAL(串行队列): 等待正在执行中的处理结束,再执行下一条处理。
     */
    //
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    //2.任务(同步、异步)
    /**
     执行任务的方式:
     同步 ->  不开启新的线程    dispatch_sync(queue, ^{  ...  });
     异步 ->  开启新的线程     dispatch_async(queue, ^{ ... });
     */
    dispatch_async(queue, ^{
       
        NSLog(@"同步执行->执行任务A---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        
        NSLog(@"同步执行->执行任务B---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        
        NSLog(@"同步执行->执行任务C---%@",[NSThread currentThread]);
    });
    
    // dispatch_barrier_sync: 在它前面的任务执行结束后它才执行,在它后面的任务等它执行完成后才会执行
    dispatch_barrier_async(queue, ^{
        for (int i = 0; i< 1000; i++) {
            if (i == 999) {
            NSLog(@"----在barrier中添加耗时操作-----%@", [NSThread currentThread]);
            }
        }
        NSLog(@"ABC 全部执行完成之后再在子线程执行三个同步任务DEF");
    });
   dispatch_async(queue, ^{
    
        NSLog(@"同步执行->执行任务D---%@",[NSThread currentThread]);

    });
    dispatch_async(queue, ^{
        NSLog(@"同步执行->执行任务E---%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"同步执行->执行任务F---%@",[NSThread currentThread]);
    });
image.png

案例3:售票的小故事(窗口1和窗口2同时售票,售完即止)

//票总数
@property(nonatomic,assign)NSInteger ticketSurplusCount;


  self.ticketSurplusCount = 30;
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
  /**
    信号量:就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。
     其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。
     
     dispatch_semaphore_create(信号量值)      创建信号量, 参数:信号量的初值,如果小于0则会返回NULL
     dispatch_semaphore_wait(信号量,等待时间) 等待降低信号量
     dispatch_semaphore_signal(信号量)         提高信号量
     注意:正常的使用顺序是先降低然后再提高,这两个函数通常成对使用
     */
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    
    while (1) {
        dispatch_async(queue, ^{
            
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

            if (self.ticketSurplusCount > 0 ) {
                
                  self.ticketSurplusCount --;
                NSLog(@"剩余票数:%zd 窗口:%@",self.ticketSurplusCount,[NSThread currentThread]);
            
            }
            dispatch_semaphore_signal(semaphore);
        });
        NSLog("%@---############---",[NSThread currentThread]);
            if (self.ticketSurplusCount <= 0) {
                
                  NSLog(@"不好意思。。票买完了。。。。");
                break;
            }
    }
image.png

相关文章

网友评论

      本文标题:iOS - 多线程三部曲之GCD(二)

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