iOS之GCD笔记二

队列的几种创建方式

  • 串行队列

dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);

serialQueue:表示队列的名称
DISPATCH_QUEUE_SERIAL:表示为串行队列

dispatch_queue_t nullQueue = dispatch_queue_create("nullQueue", NULL);

后面标注的NULL也是串行队列

dispatch_queue_t mainQueue = dispatch_get_main_queue();

主队列是一种特殊的串行队列

  • 并发队列

dispatch_queue_t concurrentQueue = dispatch_queue_create("asynQueue", DISPATCH_QUEUE_CONCURRENT);

DISPATCH_QUEUE_CONCURRENT:表示为并发队列

dispatch_queue_t highGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

并发队列+优先级高

dispatch_queue_t defaultGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"defaultGlobalQueue-----%@",defaultGlobalQueue);

并发队列+优先级默认

dispatch_queue_t lowGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    NSLog(@"lowGlobalQueue-----%@",lowGlobalQueue)

并发队列+优先级低

dispatch_queue_t backGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    NSLog(@"backGlobalQueue-----%@",backGlobalQueue);

并发队列+优先级最低

优先级高的队列里的任务会先执行,之后才执行优先级低的队列中的任务(需要注意:十次里面可能有九次都是等待优先级高的队列里的任务全部执行完毕才开始执行优先级低的队列中的任务,但是也会出现优先级高的队列中的任务没有完全执行完成就开始执行优先级低的队列中的任务)
亲测如下(测试很多次才可能出现一次):highGlobalQueue中i=9的那次打印还没执行,lowGlobalQueue中i=0的那次打印就执行了

for (int i = 0; i< 10;i++){
        dispatch_async(lowGlobalQueue, ^{
            NSLog(@"backGlobalQueue--%@--%d",[NSThread currentThread], i);
        });
        dispatch_async(highGlobalQueue, ^{
            NSLog(@"lowGlobalQueue--%@--%d",[NSThread currentThread], i);
        });
    }

打印结果如下

2018-08-22 16:35:50.621567+0800 GCD_Demo[92525:3732297] highGlobalQueue--{number = 183, name = (null)}--0
2018-08-22 16:35:50.621647+0800 GCD_Demo[92525:3732204] highGlobalQueue--{number = 174, name = (null)}--1
2018-08-22 16:35:50.621685+0800 GCD_Demo[92525:3732299] highGlobalQueue--{number = 185, name = (null)}--2
2018-08-22 16:35:50.621698+0800 GCD_Demo[92525:3732198] highGlobalQueue--{number = 169, name = (null)}--3
2018-08-22 16:35:50.621722+0800 GCD_Demo[92525:3732207] highGlobalQueue--{number = 177, name = (null)}--4
2018-08-22 16:35:50.621728+0800 GCD_Demo[92525:3732201] highGlobalQueue--{number = 171, name = (null)}--5
2018-08-22 16:35:50.621738+0800 GCD_Demo[92525:3732203] highGlobalQueue--{number = 173, name = (null)}--6
2018-08-22 16:35:50.621797+0800 GCD_Demo[92525:3732295] highGlobalQueue--{number = 181, name = (null)}--7
2018-08-22 16:35:50.621817+0800 GCD_Demo[92525:3732296] highGlobalQueue--{number = 182, name = (null)}--9
2018-08-22 16:35:50.621761+0800 GCD_Demo[92525:3732206] lowGlobalQueue--{number = 176, name = (null)}--0
2018-08-22 16:35:50.621823+0800 GCD_Demo[92525:3731738] highGlobalQueue--{number = 154, name = (null)}--8
2018-08-22 16:35:50.622011+0800 GCD_Demo[92525:3732298] lowGlobalQueue--{number = 184, name = (null)}--1
2018-08-22 16:35:50.622329+0800 GCD_Demo[92525:3732265] lowGlobalQueue--{number = 179, name = (null)}--3
2018-08-22 16:35:50.622349+0800 GCD_Demo[92525:3732294] lowGlobalQueue--{number = 180, name = (null)}--2
2018-08-22 16:35:50.622505+0800 GCD_Demo[92525:3732299] lowGlobalQueue--{number = 185, name = (null)}--5
2018-08-22 16:35:50.622406+0800 GCD_Demo[92525:3732204] lowGlobalQueue--{number = 174, name = (null)}--4
2018-08-22 16:35:50.622536+0800 GCD_Demo[92525:3732198] lowGlobalQueue--{number = 169, name = (null)}--6
2018-08-22 16:35:50.623330+0800 GCD_Demo[92525:3732207] lowGlobalQueue--{number = 177, name = (null)}--7
2018-08-22 16:35:50.623420+0800 GCD_Demo[92525:3732297] lowGlobalQueue--{number = 183, name = (null)}--8
2018-08-22 16:35:50.623996+0800 GCD_Demo[92525:3732295] lowGlobalQueue--{number = 181, name = (null)}--9

GCD的常见用法

  • dispatch_apply

该函数指定次数将指定的Block追加到指定的Queue中,并等待全部处理执行结束

第一个参数是执行的次数
第二个参数是任务加入的队列
第三个block是执行的任务,需传入一个次数的参数

dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
        NSLog(@"dispatch_apply--%@--%ld",[NSThread currentThread], index);
    });
  • dispatch_barrier

GCD栅栏函数:在并发队列中将此代码插入的地方上下隔开,如果栅栏一样,两部分不影响。只有上边的并发队列都执行结束之后,下边的并发队列才能够执行。

如下例子中:指定在任务1、2(无序)执行之后,执行任务barrier,然后执行任务3、4(无序)
特点:指定在任务1、2、3、4是在同一个队列中执行才有效

dispatch_barrier_async与dispatch_barrier_sync区别:

dispatch_barrier_sync代码后边的任务直到dispatch_barrier_sync执行完才能被追加到队列中;
dispatch_barrier_async不用代码执行完,后边的任务也会被追加到队列中。
代码上的体现就是dispatch_barrier_sync后边的代码不会执行,dispatch_barrier_async后边的代码会执行,但是Block不会被执行。

    dispatch_queue_t queue = dispatch_queue_create("testDispatch_barrier_async", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{
        NSLog(@"1-----%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"2-----%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"barrier-----%@",[NSThread currentThread]);
    });
    /*
    dispatch_barrier_sync(queue, ^{
        NSLog(@"barrier-----%@",[NSThread currentThread]);
    });
     */
    
    dispatch_async(queue, ^{
        NSLog(@"3-----%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"4-----%@",[NSThread currentThread]);
    });
    
    NSLog(@"the end");
  • dispatch_group

队列组:可以监听不同队列中的多个任务执行结束之后,进行相应的操作,以下两种法,效果是一样的
  1. 队列组用法示例,在指定任务1、2、3(无序)都执行完成之后,执行相应的操作
    特点:可以是监听多个队列下的任务都执行完毕
- (void)testDispatch_group {
    //创建队列组
    dispatch_group_t group = dispatch_group_create();
    //创建并发队列
    dispatch_queue_t firstQueue = dispatch_queue_create("firstQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t secondQueue = dispatch_queue_create("secondQueue", DISPATCH_QUEUE_CONCURRENT);

    //封装任务,添加到队列并监听任务的执行情况
    dispatch_group_async(group, firstQueue, ^{
        NSLog(@"1-----%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, firstQueue, ^{
        NSLog(@"2-----%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, secondQueue, ^{
        NSLog(@"3-----%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, secondQueue, ^{
        NSLog(@"4-----%@",[NSThread currentThread]);
    });
    
    dispatch_group_async_f(group, secondQueue, @"testText", functionWotk);
    
    /*
    拦截通知,监听到所有所有任务执行完毕,执行相应的操作
    下面的group指的是在哪个队列组,而firstQueue指的是block任务在哪个对列中执行
    dispatch_group_notify 内部是异步执行
     */
    dispatch_group_notify(group, firstQueue, ^{
        NSLog(@"the notify");
    });
    
    NSLog(@"the end");
}

void functionWotk (void * text) {
    NSLog(@"dispatch_group_async_f-----%@-----%@",[NSThread currentThread],text);
}
  1. 队列组用法示例
- (void)testDispatch_groupEnterAndLeave {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("testDispatch_groupEnterAndLeave", DISPATCH_QUEUE_CONCURRENT);
    
    /*
     在该方法后面的任务会被队列组监听
     dispatch_group_enter与dispatch_group_leave成对使用,对应一进一出
     */
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"1-----%@",[NSThread currentThread]);
        //监听到该任务执行完毕
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"2-----%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"3-----%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"the notify");
    });
}
  • dispatch_semaphore

根据信号量实现多线程同步机制,用来管理对资源的并发访问

dispatch_semaphore 有以下三个函数
dispatch_semaphore_t dispatch_semaphore_create(1);// 创建信号量,参数:信号量的初值,如果不大于0则会返回NULL

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);// 等待降低信号量,接收一个信号和时间值(多为DISPATCH_TIME_FOREVER);若信号的信号量为0,则会阻塞当前线程,直到信号量大于0或者经过输入的时间值;若信号量大于0,则会使信号量减1并返回,程序继续住下执行

long dispatch_semaphore_signal(dispatch_semaphore_t dsema);// 提高信号量, 使信号量加1并返回

下面提供的是一个出售火车票的场景用例

/**
 * 线程安全:使用 semaphore 加锁
 * 初始化火车票数量、卖票窗口(线程安全)、并开始卖票
 */
- (void)testTicketSale {
    NSLog(@"semaphore---begin");
    
    __block NSInteger ticketSurplusCount = 50;
    dispatch_semaphore_t semaphoreLock = dispatch_semaphore_create(1);

    // queue1 代表火车票售卖窗口1
    dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
    // queue2 代表火车票售卖窗2
    dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue1, ^{
        while (1) {
            dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);// 相当于加锁
            
            if (ticketSurplusCount > 0) {//如果还有票,继续售卖
                ticketSurplusCount--;
                NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:queue1",ticketSurplusCount]);
                [NSThread sleepForTimeInterval:0.2];
                dispatch_semaphore_signal(semaphoreLock);// 相当于解锁
            } else { //如果已卖完,关闭售票窗口
                NSLog(@"所有火车票均已售完");
                dispatch_semaphore_signal(semaphoreLock);// 相当于解锁
                break;
            }
        }
    });
    
    dispatch_async(queue2, ^{
        while (1) {
            dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);// 相当于加锁
            
            if (ticketSurplusCount > 0) {//如果还有票,继续售卖
                ticketSurplusCount--;
                NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:queue2",ticketSurplusCount]);
                [NSThread sleepForTimeInterval:0.2];
                dispatch_semaphore_signal(semaphoreLock);// 相当于解锁
            } else { //如果已卖完,关闭售票窗口
                NSLog(@"所有火车票均已售完");
                dispatch_semaphore_signal(semaphoreLock);// 相当于解锁
                break;
            }
        }
    });
    //当线程1执行到dispatch_semaphore_wait这一行时,semaphore的信号量为1,所以使信号量-1变为0,并且线程1继续往下执行;如果当在线程1售票操作还没执行完的时候,又有线程2来访问,执行dispatch_semaphore_wait时由于此时信号量为0,且时间为DISPATCH_TIME_FOREVER,所以会一直阻塞线程2(此时线程2处于等待状态),直到线程1执行完售票操作并且执行完dispatch_semaphore_signal使信号量为1后,线程2才能解除阻塞继续住下执行。以上可以保证同时只有一个线程执行售票操作这部分block中的代码
}

如果有地方理解错误,欢迎各位大佬指正,十分感谢!

推荐几篇本文参考的博文

iOS超级超级详细介绍GCD:对GCD的用法原理讲的通俗,容易理解
iOS多线程:『GCD』详尽总结:十分详尽
iOS多线程之GCD:对GCD API的标注很全面

你可能感兴趣的:(iOS之GCD笔记二)