OC 多线程之 GCD知识从基础到进阶 (1)

OC 多线程之 GCD知识从基础到进阶 (1)

概念认知

基本概念:

  • GCD 全称 (Grand Center Dispatch),翻译成通俗中文“牛B的中心调度机制”

  • 线程:执行任务调度的最小单位

  • 任务:就是一段代码,GCD中就是block中的内容。执行任务有两种方式:同步执行(sync)和异步执行(async)。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力

  • 队列:用来存放任务的线性结构,采用 FIFO(先进先出)的原则。

进阶概念:

  • 同步:

    • 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行后续任务。
    • 只能在当前线程中执行任务,不具备开启新线程的能力。
  • 异步:

    • 异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
    • 可以在新的线程中执行任务,具备开启新线程的能力。
  • 串行队列: 队列中的任务 按添加顺序开始执行

  • 并发队列: 队列中的任务可同时开始执行,可以开辟多个线程

  • 主队列(等待主线程内的任务完成之后再利用主线程执行任务)

  • 全局并发队列:本质上就是一个apple默认的并发队列

GCD 使用步骤

  • 创建队列:

    // 串行队列的创建方法
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
    // 并发队列的创建方法
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
    // 主队列的获取方法
    dispatch_queue_t queue = dispatch_get_main_queue();
    // 全局并发队列的获取方法
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
  • 创建任务:

    // 同步执行任务创建方法
    dispatch_sync(queue, ^{
        // 这里放同步执行任务代码
    });
    // 异步执行任务创建方法
    dispatch_async(queue, ^{
        // 这里放异步执行任务代码
    });
    

串行并发队列 + 同步异步组合

1.同步执行 + 并发队列
2.异步执行 + 并发队列
3.同步执行 + 串行队列
4.异步执行 + 串行队列
5.同步执行 + 主队列
6.异步执行 + 主队列

各种组合:


串行并发队列+同步异步组合

问题:

  • 死锁:在主线程主队列执行同步任务就会造成线程死锁。

  • 主队列中的任务一定是在主线程中执行的

  • 全局并发队列中的任务可以开辟新线程,但不一定会开辟

GCD 线程间通信

// GCD的线程间的通信 : 常用代码
- (void)GCDDemo
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"正在努力下载... %@",[NSThread currentThread]);
        // 如果下载结束回到主线程更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"下载完成,正在更新UI... %@",[NSThread currentThread]);
        });
    });
}

GCD 的其他方法

GCD 栅栏方法:dispatch_barrier_async

对于栅栏方法的理解,我觉得下面的图片很形象:


dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
dispatch_async(queue, ^{
    // 追加任务1
    for (int i = 0; i < 2; ++i) {
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
    }
});
    
dispatch_barrier_async(queue, ^{
        // 追加任务 barrier
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程
        }
    });
    
dispatch_async(queue, ^{
    // 追加任务2
    for (int i = 0; i < 2; ++i) {
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
    }
});

虽然是异步任务 栅栏也能把这两个任务给有先后顺序的隔离开来。

GCD 延时执行方法:dispatch_after

这里需要注意的是,延迟的时间是指的 延迟将任务添加到队列中。所以这个时间并不是特别准确的

/**
 * 延时执行方法 dispatch_after
 */
- (void)after {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"asyncMain---begin");
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2.0秒后异步追加任务代码到主队列,并开始执行
        NSLog(@"after---%@",[NSThread currentThread]);  // 打印当前线程
    });
}

GCD 一次性代码(只执行一次):dispatch_once

 static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只执行1次的代码(这里面默认是线程安全的)
    });

GCD 快速迭代方法:dispatch_apply

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    NSLog(@"apply---begin");
    dispatch_apply(6, queue, ^(size_t index) {
        NSLog(@"%zd---%@",index, [NSThread currentThread]);
    });
    NSLog(@"apply---end");

GCD 队列组:dispatch_group

NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"group---begin");
    
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
        NSLog(@"group---end");
    });

GCD队列组监听:dispatch_group_notify

    /*
     * 业务异步串行执行,用GCD实现的四种方式
     * 1、dispatch_group_notify(group, queue, ^{ });
     * 2、dispatch_group_async(group, backQueue, ^{ });
     * 3、dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 会有线程等待阻塞当前线程
     * 4、信号量 = 0  实现;会有线程等待阻塞当前线程
     */
    
    // 被监听的group
    dispatch_group_t group = dispatch_group_create();
    // 将要执行后续block代码的队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    
    // ----------------- 监听group执行完毕 后,在queue队列执行block代码
    dispatch_group_notify(group, queue, ^{
        // 任务代码
    });
    
    
    // ---------------- 全局并发队列
    dispatch_queue_t backQueue = dispatch_get_global_queue(0, 0);
    // 向group中添加 在某个队列中执行的任务
    dispatch_group_async(group, backQueue, ^{
        // 追加的任务
    });

    /*
     * 监听group执行完毕 之后在某个队列中执行某个任务
     * 跟 向group中追加某个队列中执行的任务 也有类似作用,但是group不会有线程等待,只是会造成先后执行的效果,但是不会有线程等待
     */

    // ------------ 在此处等待group中的任务执行完毕 然后 执行wait后面的代码,线程等待会阻塞当前线程
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    
    // 创建信号量 初始信号量0 线程等待
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    // 主队列中执行异步任务
    dispatch_async(queue, ^{
        // 信号量+1
        dispatch_semaphore_signal(semaphore);
    });
    // 信号量不为0时候-1,信号量=0时候线程等待
    // 异步任务异步执行后 直接带了wait,信号量=0 ,线程等待,等异步任务完毕时 信号量+1 不为零,此时线程不再等待,开始执行下面代码,会阻塞当前线程;
    dispatch_semaphore_wait(semaphore, 1);
    
    /*
     * semaphore = 0 时候 dispatch_semaphore_wait 等待信号量!=0,
     * 跟 dispatch_group_wait 等待group执行完毕 有一点相似。
     */

GCD中任务增删:dispatch_group_enter、dispatch_group_leave

// 向任务组添加任务
dispatch_group_enter(group);
// 将要添加的任务
dispatch_async(queue, ^{
    // 追加任务1
    [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
    NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
    // 成对出现 任务组任务执行完毕 撤销任务
    dispatch_group_leave(group);
});
// 等待group五秒钟
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)));
// 要么五秒内执行完毕走到这里;要么五秒后超时,group还未执行完 就走到了这里
NSLog(@"等待之后");
    
// 如果只有enter 没有leave那么线程会一直存在 等待group执行完毕

GCD信号量:dispatch_semaphore_t


-(void)semaphoreLockTest{
    
    dispatch_semaphore_t semaphoreLock = dispatch_semaphore_create(1);
    
    // 创建两个购票的串行队列
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
    
    __weak typeof(&*self)weakSelf = self;
    // 第一个购票队列
    dispatch_async(queue1, ^{
        [weakSelf semaphoreGetTicket:semaphoreLock];
    });
    // 第二个购票队列
    dispatch_async(queue2, ^{
        [weakSelf semaphoreGetTicket:semaphoreLock];
    });

}

-(void)semaphoreGetTicket:(dispatch_semaphore_t)semaphore{
    while (1) {
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        if (YES) {
            // 有票
            // 模拟购票延时
            [NSThread sleepForTimeInterval:0.2];
            // 相当于解锁
            dispatch_semaphore_signal(semaphore);
        }else{
            // 无票
            // 相当于解锁
            dispatch_semaphore_signal(semaphore);
            break;
        }
        
    }
}

线程锁

iOS多线程-各种线程锁的简单介绍


本文参考文章:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容