面试经典-GCD API

GCD的API:

    介绍完GCD,接下来让我们进一步看下GCD的API;

 


 

dispatch_set_target_queue:

    使用dispatch_queue_create生成的Dispatch Queue使用的都是与Global Dispath Queue默认优先级相同的执行优先级线程;

    变更生成的Dispatch Queue的执行优先级就要使用dispatch_set_target_queue函数;

    注意,第一个参数不可以指定为系统提供的Dispatch Queue;

【Code-dispatch_set_target_queueTmp】

/*dispatch_set_target_queue*/
-(void)dispatch_set_target_queueTmp{
    //需要变更优先级的Dispatch Queue指定为第一个参数
    dispatch_queue_t serialQueue = dispatch_queue_create("com.example.test1", NULL);
    
    //指定与要使用的执行优先级相同优先级的Dispatch Queue为第二个参数
    dispatch_queue_t globalQueueBackgroundPriority = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

    //将serialQueue的默认执行优先级变更为与globalQueueBackgroundPriority一样的后台执行优先级
    dispatch_set_target_queue(serialQueue, globalQueueBackgroundPriority);
}

    虽然,表面上我们只是变更了Dispatch Queue的执行优先级,其实还改变了Dispatch Queue的执行阶层;

    为什么这么说呢,其实还是优先级起的作用,比如相同优先级的两个Dispatch Queue,在同一时刻只能执行一个Dispatch Queue的处理;

    

    我们之前知道,多个Serial Dispatch Queue是并行处理的,如果将他们使用dispatch_set_target_queue函数指定为某一Serial Dispatch Queue,这样就可以防止他们并行执行了;

 

 


 

dispatch_after:

    想要在指定时间(如3s)后执行处理的情况,可以使用dispatch_after函数实现;

【Code-dispatch_afterTmp】

/*dispatch_after*/
-(void)dispatch_afterTmp{
    dispatch_time_t time  = dispatch_time(DISPATCH_TIME_NOW, 3ull*NSEC_PER_SEC);
    
    dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"等待了3s后 执行!");
    });
}

    注意此函数,是在指定时间(从现在起3秒的时刻)追加处理到Dispatch Queue;这与3s后再追加处理处理是一样的;

    这与3s后执行是不一样的:

        这个例子是在主线程RunLoop中执行的,假如每隔1/60秒执行RunLoop,追加处理的Block最快需3s,最慢需要3+1/60后执行;

        而且Main Dispatch Queue中还有大量的追加处理以及主线程本身的延迟等;

    但是,在大致的时间延至时,使用这个还是很有效率的;

 

dispatch_time_t类型:

    这个类型的值使用dispatch_time函数或dispatch_walltime函数作成;

 

dispatch_time函数:

    相对时间计算,dispatch_time_t time  = dispatch_time(DISPATCH_TIME_NOW, 3ull*NSEC_PER_SEC);

    这个函数可以获取从第一个参数dispatch_time_t类型值指定的实践开始,到第二个参数指定的毫微妙单位后的时间;

    数值与NSEC_PER_SEC相乘得到毫微妙的数值;ull是C语言的数值字面量,显示指明类型(ull-unsigned long long);

    如果使用3ull*NSEC_PER_MSEC,表示的就是3毫秒了;

 

dispatch_walltime函数:

    绝对时间计算,适合在dispatch_after函数中指定xx年xx月xx日xx时xx分xx秒这一绝对时间处理任务;

【Code-dispatch_walltimeTmp】

/*dispatch_walltime*/
dispatch_time_t dispatch_walltimeTmp(NSDate * date){
    NSTimeInterval interval;
    double second, subsecond;
    struct timespec time;
    dispatch_time_t milestone;
    
    //
    interval = [date timeIntervalSince1970];
    subsecond = modf(interval, &second);
    time.tv_sec = second;
    time.tv_nsec = subsecond * NSEC_PER_SEC;
    milestone = dispatch_walltime(&time, 0);
    
    return milestone;
}
-(void)dispatch_walltimeTmp{
    NSTimeInterval currentTime= [[NSDate date] timeIntervalSince1970];
    NSDate * date = [NSDate dateWithTimeIntervalSince1970:(currentTime + 3600)];//3600s 即1分钟后的具体时间戳
    
    dispatch_time_t time  = dispatch_walltimeTmp(date);
    
    dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"等待了1s后 执行!");
    });
}

 

Dispatch Group:

    在追加到Dispatch Queue中的多个处理都完成之后,想要执行结束处理的需求场景;如果只是Serial Dispatch Queue直接将结束处理追加即可;

    但是对于Concurrent Dispatch Queue或同时使用多个Dispatch Queue时,就需要使用Dispatch Group了;

    无论向Dispatch Queue中追加什么样的处理,使用Dispatch Group都可以监视这些处理执行的结束;

    一旦结束,就可以将结束的处理追加到指定的Dispatch Queue中;

【Code-dispatch_groupTmp】

/*dispatch_groupTmp*/
-(void)dispatch_groupTmp{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-1");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-2");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-3");
    });
    //通过dispatch_group_enter和dispatch_group_leave
    //——并不能保证通过dispatch_group_async追加处理的执行顺序(5...4...6)
    dispatch_group_enter(group);//我要通过group追加处理了
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-4");
        dispatch_group_leave(group);//当前的处理结束了 我要通过group移除已完成的这个处理了
    });
    //
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-5");
        dispatch_group_leave(group);
    });
    //
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-6");
        dispatch_group_leave(group);
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"Done");
    });

}

    dispatch_group_async与dispatch_asyng一样,都是追加Block到指定的Dispatch Queue中;

    dispatch_group_notify函数会将执行的Block追加到Dispatch Queue中,它的第一个参数指定要监视的Dispatch Group;

 

dispatch_group_wait:

    Dispatch Group中也可以使用dispatch_group_wait函数等待全部处理执行结束;

【Code-dispatch_group_waitTmp1】

/*dispatch_group_wait*/
-(void)dispatch_group_waitTmp1{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-1");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-2");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-3");
    });
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//一直等待 知道所有的处理结束 中途不能取消
    NSLog(@"Done");
}

【Code-dispatch_group_waitTmp2】

-(void)dispatch_group_waitTmp2{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-1");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-2");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"bkl-3");
    });
    
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
    long result = dispatch_group_wait(group, time);//等待1s

    if (result == 0) {
        NSLog(@"Done");
    }else{
        NSLog(@"Doing");
    }
}

    一旦调用dispatch_group_wait函数,当前的线程就会等待,直到指定时间结束或所有处理都已结束,线程结束;

    在第二个示例中,如果直接指定DISPATCH_TIME_NOW,则不用任何等待即可判定属于Dispatch Group的处理是否都已结束;

 


 

dispatch_barrier_async:

    像栅栏一样的函数:

        我们知道访问数据库或文件时,使用Serial Dispatch Queue可以避免数据竞争问题;

        为了高效率的访问,我们可以将读的处理追加到Concurrent Dispatch Queue,而写的处理依旧使用Serial Dispatch Queue;

        相应的场景虽然可以使用使用之前介绍的API实现,但是GCD也提供了更加聪明的解决方案:dispatch_barrier_async函数;

【Code-dispatch_barrier_asyncTmp】

/*dispatch_barrier_async*/
-(void)dispatch_barrier_asyncTmp{
    void (^blk1Read)(void) = ^{NSLog(@"blk1Read");};
    void (^blk2Read)(void) = ^{NSLog(@"blk2Read");};
    void (^blk3Read)(void) = ^{NSLog(@"blk3Read");};
    void (^blk4Write)(void) = ^{NSLog(@"blk4Write");};
    void (^blk5Read)(void) = ^{NSLog(@"blk5Read");};
    void (^blk6Read)(void) = ^{NSLog(@"blk6Read");};
    void (^blk7Read)(void) = ^{NSLog(@"blk7Read");};
    
    dispatch_queue_t queue = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, blk1Read);
    dispatch_async(queue, blk2Read);
    dispatch_async(queue, blk3Read);
    
//    dispatch_async(queue, blk4Write);
    dispatch_barrier_async(queue, blk4Write);
    
    dispatch_async(queue, blk5Read);
    dispatch_async(queue, blk6Read);
    dispatch_async(queue, blk7Read);

}

    该函数与dispatch_queue_create生成的Concurrent Dispatch Queue一起使用;

    在上述示例中,我们想使写入的数据,在bkl5-7read的处理中生效,如果不使用此函数,会出现许多问题,存在多个写入时更是如此;

 

dispatch_barrier_async函数的作用:

    dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue上的并行执行处理全部结束;

    再将指定的处理追加到该Concurrent Dispatch Queue中;

    然后由dispatch_barrier_async函数追加的处理执行完毕后,才将Concurrent Dispatch Queue恢复为一般动作;

    这样追加到Concurrent Dispatch Queue的后续处理又开始并行执行了;

bkl-1-read

 

bkl-4-write

 

bkl-5-read

bkl-2-read

bkl-6-read

bkl-3-read

bkl-7-read

    仅使用dispatch_barrier_async代替dispatch_async即可,且使用dispatch_barrier_async和Concurrent Dispatch Queue可以高效的实现数据库访问和文件访问;

 


 

dispatch_sync:

    dispatch_async表示“非同步”,不等待处理执行结束;

    dispatch_sync则表示“同步”,在追加的Block结束之前,会一直等待;

 

    和dispatch_group_wait类似,这里的“等待”,意味着当前线程停止;

 

死锁问题:

dispatch_sync使用简单,也容易引入死锁问题,使用时一定要慎重:最好在所有的处理结束之后,同步处理结果时再使用;

【Code-deadlockTmp1】

/*deadlock*/
-(void)deadlockTmp1{
    dispatch_queue_t queue = dispatch_get_main_queue();//serial dispatch queue均会发生死锁
    //没办法等待当前正在执行的处理结束,因为当前的这个处理就是运行这段代码;
    dispatch_sync(queue, ^{
        NSLog(@"What?");
    });
}

【Code-deadlockTmp2】

-(void)deadlockTmp2{
    dispatch_queue_t queue = dispatch_get_main_queue();//serial dispatch queue均会发生死锁
    dispatch_async(queue, ^{
        //内侧的Block等待它所在的Block执行处理结束,也会发生死锁;
        dispatch_sync(queue, ^{
            NSLog(@"What?");
        });
    });
}

 


 

dispatch_apply:

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

    这个和dispatch_sync类似(dispatch_apply只是等待本身Block的处理结束);

    由于会等待处理执行结束,因此推荐非同步的执行此函数;

【Code-dispatch_applyTmp】

/*dispatch_apply*/
-(void)dispatch_applyTmp{
    NSArray * array = @[@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10"];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //在Concurrent Dispatch Queue中非同步执行
    dispatch_async(queue, ^{
        //等待全部处理结束
        dispatch_apply(10, queue, ^(size_t index) {
            //并列处理array全部对象
            NSLog(@"%zu-%@",index,array[index]);
        });
        
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"done");
        });
    });

}

 

dispatch_suspend/dispatch_resume:

    当追加大量处理时,如果需要在中间某个Block捕获到需要的结果时,暂时不继续接下来的处理,就可以使用dispatch_suspend函数;

    当需要执行的时候,可以使用dispatch_resume恢复后续的处理;

    操作的是具体的queue:

        dispatch_suspend(queue);

        dispatch_resume(queue);

    这下函数对已经执行的处理没有影响;

        挂起后,追加到Dispatch Queue中但尚未执行的处理在此之后停止执行;

        而恢复则使得这些处理能够继续执行;

 

 


 

Dispatch Semophore:

    当我们使用如下方式,不考虑顺序将数据追加到MSMutableArray中时,执行后由内存错误导致应用程序异常结束的概率较高;

    这个时候就需要Dispatch Semaphore;

【Code-dispatch_semophore_Problem】

/*dispatch semaphore*/
-(void)dispatch_semophore_Problem{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSMutableArray * array = @[].mutableCopy;
    //出现 由内存错误导致的问题概率较高
    for (int i = 0; i<100; i++) {
        dispatch_async(queue, ^{
            [array addObject:[NSNumber numberWithInt:i]];
        });
    }
}

Dispatch Semophore是持有计数的信号:

    该计数是多线程编程中的计数类型信号;计数=0等待,计数>0时,可减1,不等待;

【Code-dispatch_semophoreTmp1】

-(void)dispatch_semophoreTmp1{
    //生成一个Semaphore 将计数值初始化为1(保证执行处理的线程同时只有一个)
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    
    //dispatch_semaphore_wait等待当前semaphore信号值大于等于1,其第二个参数指定等待时间
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
}

【Code-dispatch_semophoreTmp2】

-(void)dispatch_semophoreTmp2{
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
    
    //生成一个Semaphore 将计数值初始化为1(保证执行处理的线程同时只有一个)
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    
    //dispatch_semaphore_wait等待当前semaphore信号值大于等于1,其第二个参数指定等待时间
    long result = dispatch_semaphore_wait(semaphore, time);
    
    if (result == 0) {
        //等待到了指定的时间 且 计数值>=1,所以计数值减1,并执行需要的排他处理
        
        //该处理结束时需要使用dispatch_semaphore_signal函数将Dispatch Semaphore计数值加1
        dispatch_semaphore_signal(semaphore);
    }else{
        //计数值=0,达到指定时间后 待机
        
    }
}

现在让我们使用Dispatch Semophore解决下dispatch_semophore_Problem中可能出现的问题:

    在没有Serial Dispatch Queue和dispatch_barrier_async函数那么大粒度 且 一部分处理需要进行排他控制时,Dispatch Semophore很有用;

【Code-dispatch_semophoreTmp3】

-(void)dispatch_semophoreTmp3{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //Dispatch Semaphore计数初始值设为1,保证可访问NSMutableArray类的线程同时只能有一个
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    
    NSMutableArray * array = @[].mutableCopy;
    //出现 由内存错误导致的问题概率较高
    for (int i = 0; i<100; i++) {
        dispatch_async(queue, ^{
            //等待信号量的计数值>=1
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            
            //等到了计数值=1,代码执行到此 将计数值-1,此时为0;使用一个线程安全的进行array更新;
            [array addObject:[NSNumber numberWithInt:i]];
            
            //排他控制处理结束,通过dispatch_semaphore_signal函数,将计数值+1
            dispatch_semaphore_signal(semaphore);
        });
    }
}

 

dispatch_once:

    该函数保证程序只执行一次指定处理;

    在生成单例对象时使用;

【Code-dispatch_once_problem】

/*dispatch_once*/
-(void)dispatch_once_problem{
    static int initialized = NO;
    if (initialized == NO) {
        //初始化(多数i情况下还是安全的 但在多核CPU中 可能会出现初始化多次的情形)
        
        initialized = YES;
    }
}

【Code-dispatch_onceTmp】

-(void)dispatch_onceTmp{
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
       //初始化(优点:多线程下执行 依然安全)
        
    });
}

 

Dispatch I/O:

    适用于读取较大文件时,一次使用多个线程并列读取,效率更高(写与读类似);

    实现此功能的就是Dispatch I/O和Dispatch Data;

 

Dispatch Data:

    分割和读取数据通过使用Dispatch Data可更为简单地进行结合和分割;

 

注:主要函数dispatch_io_create()等,在为了提高文件读取速度时可以使用,没有实现示例,需要的同学自行查阅。

 


Dispatch Source:

    这个Dispatch Source有许多种类的事件,当对应的事件发生时,可以在指定的Dispatch Queue中执行事件的处理;

    如果说Dispatch Queue是我们注定追加处理,那么Dispatch Source则是设定了具体的事件场景来在指定的Dispatch Queue中处理事件发生时的任务;

【Code-dispatch_sourceTmp】

/*Dispatch Source*/
-(void)dispatch_sourceTmp{
    
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    //timer定时器 的起始时间为15s之后
    //timer定时器 的执行间隔为DISPATCH_TIME_FOREVER 即不重复
    //timer定时器 的允许的时间延迟是1s;
    dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 15ull*NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 1ull*NSEC_PER_SEC);
    
    //指定定时器指定时间内执行的处理
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"do something");
        
        dispatch_source_cancel(timer);
    });
    
    //指定取消Dispatch Sourced时的处理
    dispatch_source_set_cancel_handler(timer, ^{
        NSLog(@"do cancel");

    });
    
    //启动 该定时器(Dispatch Sourced)
    dispatch_resume(timer);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值