多线程之GCD

GCD(Grand Central Dispatch) 介绍

GCD属于系统级的线程管理,在Dispatch queue中执行需要执行的任务性能非常的高。GCD中的FIFO队列称为dispatch queue,用来保证先进来的任务先得到执行。

1、基本概念

1、执行任务的方式
①同步 sync

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

②异步 async

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

2、队列

系统级:
①dispatch_get_global_queue 全局队列,一个并行的队列
②dispatch_get_main_queue 主队列,主线程中的唯一队列,一个串行队列

自定义:
dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr)

2、介绍

1、dispatch_get_global_queue(long identifier, unsigned long flags)

全局并发队列,系统级:dispatch_get_global_queue

参数:
identifier:优先级,只是CPU调用的级别不同,不保证前后顺序
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
flags:保留值。填0

注:

DISPATCH_QUEUE_PRIORITY_HIGH映射到QOS_CLASS_USER_INITIATED类。
DISPATCH_QUEUE_PRIORITY_DEFAULT映射到QOS_CLASS_DEFAULT类。
DISPATCH_QUEUE_PRIORITY_LOW映射到QOS_CLASS_UTILITY类。
DISPATCH_QUEUE_PRIORITY_BACKGROUND映射到QOS_CLASS_BACKGROUND类。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2、dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr)

参数:
label:const char * 类型 标签,可选,可为NULL
attr:
串行 DISPATCH_QUEUE_SERIAL或NULL(开辟一个子线程,任务在这个线程里顺序执行)
并发 DISPATCH_QUEUE_CONCURRENT(开辟多个子线程,任务在各个线程里并发执行)

dispatch_queue_t queue = dispatch_queue_create("xixi", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
  NSLog(@"%@",[NSThread currentThread]);
});

也可以给自定义队列设置优先级,有两种方式:dispatch_queue_attr_make_with_qos_class、dispatch_set_target_queue

dispatch_queue_attr_make_with_qos_class

attr:同上

qos_class:

QOS_CLASS_USER_INTERACTIVE:user interactive等级表示任务需要被立即执行提供好的体验,用来更新UI,响应事件等。这个等级最好保持小规模。
QOS_CLASS_USER_INITIATED:user initiated等级表示任务由UI发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。
QOS_CLASS_UTILITY:utility等级表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。
QOS_CLASS_BACKGROUND:background等级表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。

relative_priority:负偏移量 该值必须小于0且大于或等于QOS_MIN_RELATIVE_PRIORITY,否则此函数返回NULL(QOS_MIN_RELATIVE_PRIORITY是-15)

dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_DEFAULT, -1);
dispatch_queue_t queue = dispatch_queue_create("xixi",attr);
dispatch_async(queue, ^{
  NSLog(@"%@",[NSThread currentThread]);
});
dispatch_set_target_queue
dispatch_queue_t queue = dispatch_queue_create("xixi",NULL); //需要设置优先级的queue
dispatch_queue_t tQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //参考优先级
dispatch_set_target_queue(queue, tQueue); //设置queue和tQueue的优先级一样
dispatch_async(queue, ^{
    NSLog(@"%@",[NSThread currentThread]);
});

dispatch_set_target_queue:可以设置优先级,也可以设置队列层级体系,比如让多个串行和并行队列在统一一个串行队列里串行执行,如下

    dispatch_queue_t serialQueue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t firstQueue = dispatch_queue_create("firstqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t secondQueue = dispatch_queue_create("secondqueue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_set_target_queue(firstQueue, serialQueue);
    dispatch_set_target_queue(secondQueue, serialQueue);

    dispatch_async(firstQueue, ^{
        NSLog(@"1");
        [NSThread sleepForTimeInterval:3.f];
    });
    dispatch_async(secondQueue, ^{
        NSLog(@"2");
        [NSThread sleepForTimeInterval:2.f];
    });
    dispatch_async(secondQueue, ^{
        NSLog(@"3");
        [NSThread sleepForTimeInterval:1.f];
    });
3、dispatch_after

延时提交block,不会阻塞线程
参数:
dispatch_time_t when:dispatch_time(dispatch_time_t when, int64_t delta)
when有两个值:
DISPATCH_TIME_NOW 当前
DISPATCH_TIME_FOREVER 遥远的未来

delta:纳秒
#define NSEC_PER_SEC 1000000000ull //每秒有多少纳秒
#define USEC_PER_SEC 1000000ull //每秒有多少毫秒
#define NSEC_PER_USEC 1000ull //每毫秒有多少纳秒

1秒可以这样写
dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);

dispatch_queue_t queue:队列
dispatch_block_t block:block

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
     // 

    });
4、dispatch_barrier_async

栅栏函数,用于提交的闭包是指定队列中在特定时段唯一在执行的一个。

注:只在自己创建的队列上有这种作用,在全局并发队列和串行队列上,效果和dispatch_sync一样
dispatch_queue_t dataQueue = dispatch_queue_create("dataqueue", DISPATCH_QUEUE_CONCURRENT);
    // 先执行这两步
    dispatch_async(dataQueue, ^{
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"read data 1 %@",[NSThread currentThread]);
    });
    dispatch_async(dataQueue, ^{
        NSLog(@"read data 2 %@",[NSThread currentThread]);
    });

    // 前面执行完才执行这步
    dispatch_barrier_async(dataQueue, ^{
        NSLog(@"write data 1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1];
    });
    //再执行这步
    dispatch_barrier_async(dataQueue, ^{
        NSLog(@"write data 2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1];
    });
    // 最后再执行这两步
    dispatch_async(dataQueue, ^{
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"read data 3 %@",[NSThread currentThread]);

    });
    dispatch_async(dataQueue, ^{
        NSLog(@"read data 4 %@",[NSThread currentThread]);
    });
5、dispatch_apply

快速迭代

dispatch_get_main_queue会死锁
阻塞当前线程

 NSLog(@"开始%@",[NSThread currentThread]);
    dispatch_apply(10,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {
        NSLog(@"%zu %@",i,[NSThread currentThread]);
    });
    NSLog(@"结束%@",[NSThread currentThread]);
6、dispatch_group_t

dispatch groups是专门用来监视多个异步任务。dispatch_group_t实例用来追踪不同队列中的不同任务。
当group里所有事件都完成GCD API有两种方式发送通知,第一种是dispatch_group_wait,会阻塞当前进程,等所有任务都完成或等待超时。第二种方法是使用dispatch_group_notify,异步执行闭包,不会阻塞。

dispatch_queue_t conQueue = dispatch_queue_create("xx", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, conQueue, ^{
        NSLog(@"1 %@",[NSThread currentThread]);
    });
    dispatch_group_async(group, conQueue, ^{
        NSLog(@"2 %@",[NSThread currentThread]);
    });
    //阻塞
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//    dispatch_group_notify(group, conQueue, ^{
//        NSLog(@"end %@",[NSThread currentThread]);
//    });
    NSLog(@"go on %@",[NSThread currentThread]);
注:
dispatch_group_async等价于dispatch_group_enter() 和 dispatch_group_leave()的组合。
dispatch_group_enter() 必须运行在 dispatch_group_leave() 之前。
dispatch_group_enter() 和 dispatch_group_leave() 需要成对出现的
7、dispatch_semaphore_t

信号量:可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,可告知系统按照我们指定的信号量数量来执行多个线程。

其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。

//创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)
//等待降低信号量
dispatch_semaphore_wait(信号量,等待时间) -1
//提高信号量
dispatch_semaphore_signal(信号量)  +1

dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //任务1
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task1");
        sleep(1);
        NSLog(@"end task1");
        dispatch_semaphore_signal(semaphore);
    });
    //任务2
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
         NSLog(@"run task2");
        sleep(1);
        NSLog(@"end task2");
        dispatch_semaphore_signal(semaphore);
    });
    //任务3
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task3");
        sleep(1);
       NSLog(@"end task3");
        dispatch_semaphore_signal(semaphore);
    });

控制台信息

2018-04-17 14:44:31.909536+0800 test[3543:656486] run task1
2018-04-17 14:44:31.909536+0800 test[3543:656487] run task2
2018-04-17 14:44:32.912763+0800 test[3543:656486] end task1
2018-04-17 14:44:32.912784+0800 test[3543:656487] end task2
2018-04-17 14:44:32.912999+0800 test[3543:656488] run task3
2018-04-17 14:44:33.916648+0800 test[3543:656488] end task3

可以看出,我们设置的信号量为2,任务最多执行两个。当某个任务完成采取执行任务3

参考文章:细说GCD(Grand Central Dispatch)如何用

你可能感兴趣的:(多线程之GCD)