GCD

来源:互联网 发布:js设置div的margin 编辑:程序博客网 时间:2024/06/15 15:54

GCD 常用的几种语句:


dispatch_once_t必须是全局或static变量

dispatch_queue_create,创建队列用的


dispatch_after是延迟提交,不是延迟运行


dispatch_time_t


dispatch_suspend != 立即停止队列的运行


“同步”的dispatch_apply


dispatch_group


1、dispatch_once_t的使用。注意下 非全局或非static的dispatch_once_t变量在使用时会导致非常不好排查的bug

    /**

    * 单例

    */

    //声明静态变量或者全局变量

    staticdispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

    //实例对象的代码

    });

2、dispatch_queue_t dispatch_queue_create ( const char *label, dispatch_queue_attr_t attr ); 有两个参数 ,第一个字符窜的标记 第二个dispatch_queue_attr_t类型 的常量,一般有两个 DISPATCH_QUEUE_SERIAL  串行的、DISPATCH_QUEUE_CONCURRENT 并发的

使用

   //创建串行队列

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

    //创建并发队列

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


队列一般和线程联合使用

  //创建串行队列

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

    //异步线程

    dispatch_async(serialqueue, ^{

        //在线程中的操作如加载图片

    });

    //创建并发队列

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

    //同步线程

    dispatch_sync(concurrentqueue, ^{

       //在线程中的操作如加载图片

    });


3、dispatch_after是延迟提交,不是延迟运行。官方文档 指的就是将一个Block在特定的延时以后,加入到指定的队列中,不是在特定的时间后立即运行!

举个例子

   //创建并发队列

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

    NSLog(@"我开始进入线程");

    //同步线程

    dispatch_async(concurrentqueue, ^{

       //在线程中的操作如加载图片

        NSLog(@"我在线程中,将要进行睡眠5");

        //休眠语句

        [NSThreadsleepForTimeInterval:5];

        NSLog(@"我在线程中,已经睡眠5");

    });

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

        NSLog(@"1秒我将after");

    });


2017-10-23 17:00:15.363 NewGCD[805:43858] 我开始进入线程

2017-10-23 17:00:15.364 NewGCD[805:43904] 我在线程中,将要进行睡眠5

2017-10-23 17:00:16.456 NewGCD[805:43921] 1秒我将after

2017-10-23 17:00:20.369 NewGCD[805:43904] 我在线程中,已经睡眠5


   //创建并发队列

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

    NSLog(@"我开始进入线程");

    //同步线程

    dispatch_async(concurrentqueue, ^{

       //在线程中的操作如加载图片

        NSLog(@"我在线程中,将要进行睡眠5");

        //休眠语句

        //[NSThread sleepForTimeInterval:5];❤️

        NSLog(@"我在线程中,已经睡眠5");

    });

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

        NSLog(@"1秒我将after");

    });

2017-10-23 17:13:42.448 NewGCD[825:46635] 我开始进入线程

2017-10-23 17:13:42.448 NewGCD[825:46684] 我在线程中,将要进行睡眠5

2017-10-23 17:13:42.449 NewGCD[825:46684] 我在线程中,已经睡眠5

2017-10-23 17:13:43.547 NewGCD[825:46667] 1秒我将after


注意这里after在线程休眠它就会插入到休眠语句那   没有就等上个线程完成,在after。 注意after的时间

dispatch_time_t 和after一起连用

dispatch_time_t time1 = dispatch_time ( dispatch_time_t when, int64_t delta );

                                                                                                    从什么时间开始DISPATCH_TIME_NOW   时间间隔

  • NSEC:纳秒。

  • USEC:微妙。

  • SEC:秒

  • PER:每


    #define NSEC_PER_SEC 1000000000ull   9      0的个数
    #define USEC_PER_SEC 1000000ull         6
    #define NSEC_PER_USEC 1000ull            3

  1. NSEC_PER_SEC,每秒有多少纳秒。                                                            秒

  2. USEC_PER_SEC,每秒有多少毫秒。(注意是指在纳秒的基础上)             毫秒

  3. NSEC_PER_USEC,每毫秒有多少纳秒。                                        

4、dispatch_suspend   不是立即停止线程运行   dispatch_resume恢复 与dispatch_suspend连用

  dispatch_queue_t concurrentqueue =dispatch_queue_create("concurrent",DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(concurrentqueue, ^{

        [NSThreadsleepForTimeInterval:5];

        NSLog(@"延时5s后打印");

    });

    dispatch_async(concurrentqueue, ^{

        [NSThreadsleepForTimeInterval:5];

        NSLog(@"在上一个延时基础上,在延时5s");

    });

    NSLog(@"准备接下来1s休眠");

    [NSThreadsleepForTimeInterval:1];

    //

    NSLog(@"开始挂起concurrentqueue");

    dispatch_suspend(concurrentqueue);

    NSLog(@"开始延迟10s");

    [NSThreadsleepForTimeInterval:10];

    NSLog(@"开始恢复concurrentqueue");

    dispatch_resume(concurrentqueue);

注意观察时间   和你用的什么队列

2017-10-23 17:41:33.362 NewGCD[918:58023] 准备接下来1s休眠

2017-10-23 17:41:34.363 NewGCD[918:58023] 开始挂起concurrentqueue

2017-10-23 17:41:34.364 NewGCD[918:58023] 开始延迟10s

2017-10-23 17:41:38.367 NewGCD[918:58062] 延时5s后打印

2017-10-23 17:41:38.367 NewGCD[918:58079] 在上一个延时基础上,在延时5s

2017-10-23 17:41:44.365 NewGCD[918:58023] 开始恢复concurrentqueue

从第一个nslog的时间和第一个线程休眠5s的时间之差是多少秒吗。   5s说明什么    说明你都dispatch_suspend但不影响dispatch_async。我这用的并发队列不是很明显,你们自己改成串行队列 就可以很清楚的看见。 

总体想说的是dispatch_suspend 不是停止正在进行的线程,要等正在执行的执行完后停止,等着恢复dispatch_resume


5、dispatch_apply

dispatch_apply的作用是在一个队列(串行或并行)上“运行”多次block,其实就是简化了用循环去向队列依次添加block任务。起个循环的作用

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

  dispatch_apply(4, serialqueue, ^(size_t i) {

      NSLog(@"执行%ld",i);

  });

    NSLog(@"执行完");

2017-10-23 17:58:18.898 NewGCD[944:62741] 执行0

2017-10-23 17:58:18.899 NewGCD[944:62741] 执行1

2017-10-23 17:58:18.899 NewGCD[944:62741] 执行2

2017-10-23 17:58:18.899 NewGCD[944:62741] 执行3

2017-10-23 17:58:18.899 NewGCD[944:62741] 执行完

都是同时进行的从时间上看。dispatch_apply将外面的线程(main线程)“阻塞


dispatch_sync导致的死锁

//在main线程使用“同步”方法提交Block,必定会死锁。
dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"I am block...");
});

- (void)updateUI1 {
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"Update ui 1");
         
        //死锁!
        [self updateUI2];
    });
}
- (void)updateUI2 {
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"Update ui 2");
    });
}

在你不注意的时候,嵌套调用可能就会造成死锁

dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
        
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@"apply loop: %zu", i);
    
    //再来一个dispatch_apply!死锁!      
dispatch_apply(3, queue, ^(size_t j) {
NSLog(@"apply loop inside %zu", j);
});
});
dispatch_apply导致的死锁

6、dispatch_group

如果是有序的任务,可以分步骤完成的,直接使用串行队列就行。但是如果是一系列并行执行的任务呢?这个时候,就需要dispatch_group帮忙了~(收尾工作)

dispatch_group的使用分如下几步:

  1. 创建dispatch_group_t

  2. 添加任务(block)

  3. 添加结束任务(如清理操作、通知UI等)

    dispatch_group_t group =dispatch_group_create();

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

    dispatch_group_async(group, queue, ^{

       //do  some

    });

af需要联合dispatch_group_enter,dispatch_group_leave

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//Enter group
dispatch_group_enter(group);
[manager GET:@"http://www.baidu.com" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    //Deal with result...
    //Leave group
    dispatch_group_leave(group);
}    failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    //Deal with error...
    //Leave group
    dispatch_group_leave(group);
}];

添加结束任务也可以分为两种情况,如下:

  1. 在当前线程阻塞的同步等待:dispatch_group_wait。

  2. 添加一个异步执行的任务作为结束任务:dispatch_group_notify

    dispatch_group_t group =dispatch_group_create();

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

    dispatch_group_async(group, queue, ^{

       //do  some

    });

    //结束group

    dispatch_group_wait(group,DISPATCH_TIME_NOW);

    dispatch_queue_t queue2 =dispatch_queue_create("DISPATCH_QUEUE_CONCURRENT",DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_notify(group, queue2, ^{

        

    });


dispatch_barrier_async  简单来说就是插入一个线程,如果你插入的时候有线程在运行,那就等线程执行完后在执行dispatch_barrier_async 在执行插入处下一个任务

值得注意的是:

dispatchbarrier\(a)sync只在自己创建的并发队列上有效,在全局(Global)并发队列、串行队列上,效果跟dispatch_(a)sync效果一样。

dispatch_set_context与dispatch_set_finalizer_f的配合使用

dispatch_set_context可以为队列添加上下文数据,但是因为GCD是C语言接口形式的,所以其context参数类型是“void *”。也就是说,我们创建context时有如下几种选择:

用C语言的malloc创建context数据。

用C++的new创建类对象。

用Objective-C的对象,但是要用__bridge等关键字转为Core Foundation对象。

以上所有创建context的方法都有一个必须的要求,就是都要释放内存!,无论是用free、delete还是CF的CFRelease,我们都要确保在队列不用的时候,释放context的内存,否则就会造成内存泄露。

所以,使用dispatch_set_context的时候,最好结合dispatch_set_finalizer_f使用,为队列设置“析构函数”,在这个函数里面释放内存,大致如下:

1
2
3
4
5
6
7
8
9
void cleanStaff(void *context) {
    //释放context的内存!
    //CFRelease(context);
    //free(context);
    //delete context;
}
...
//在队列创建后,设置其“析构函数”
dispatch_set_finalizer_f(queue, cleanStaff);


就是在清除context的函数里面,用“_bridge_transfer”转换context,把context的内存管理权限重新交给ARC,这样,就不用显式调用“CFRelease”了

void cleanStaff(void *context) {
//这里用_bridge_transfer转换,将内存管理权限交还给ARC
Data *data = (_bridge_transfer Data *)(context);
NSLog(@"In clean, context number: %d", data.number);
//不用显式释放context的内存!
}

我们都知道,GCD的接口参数都是“C语言类型“的,那么,我们如何将NSObject类型(Foundation框架)的数据,传入GCD的接口呢?即Core Foundation和Foundation对象的转换


//设置context
void dispatch_set_context (dispatch_object_t object, void *context);
//获取context
void* dispatch_get_context (dispatch_object_t object);
两个函数分别完成了将context“绑定”到特定GCD队列和从GCD队列获取对应context的任务。

dispatch_object_t object通过dispatch_queue_create创建的队列


//定义context,即一个结构体
typedef struct _Data {
int number;
} Data;
//定义队列的finalizer函数,用于释放context内存
void cleanStaff(void *context) {
NSLog(@"In clean, context number: %d", ((Data *)context)->number);
//释放,如果是new出来的对象,就要用delete
free(context);
}
- (void)runTest {
//创建队列
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
//创建Data类型context数据并初始化
Data *myData = malloc(sizeof(Data));
myData->number = 10;
//绑定context
dispatch_set_context(queue, myData);
//设置finalizer函数,用于在队列执行完成后释放对应context内存
dispatch_set_finalizer_f(queue, &cleanStaff);
dispatch_async(queue, ^{
//获取队列的context数据
Data *data = dispatch_get_context(queue);
//打印
NSLog(@"1: context number: %d", data->number);
//修改context保存的数据
data->number = 20;
});
}

  • 在dispatch_set_context的时候用__bridge_retained转换,将context的内存管理权从ARC移除,交给我们自己管理。
  • 在队列任务中,用dispatch_get_context获取context的时候,用__bridge转换,维持context的内存管理权不变,防止出了作用域context被释放。
  • 最后用CFRelease释放context内存。
  • __bridge: 只做了类型转换,不修改内存管理权;
  • __bridge_retained(即CFBridgingRetain)转换类型,同时将内存管理权从ARC中移除,后面需要使用CFRelease来释放对象;
  • __bridge_transfer(即CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将内存管理权交给ARC。


























原创粉丝点击