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

面朝大海 春暖花开

对于太多数人来说,可能都听过,也有可能在项目中用到,对于初学者来说,似乎可能没多大的用户,于是在同步、异步、串行、并行和死锁当中渐渐滴放弃治疗,接下来本文将会彻底长谈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:@"http://upload-images.jianshu.io/upload_images/1658521-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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容