GCD基本使用

GCD两个基本知识:任务和队列;

任务执行方式分为两种:同步(sync)和异步(async)
队列也分为两种:并发队列和串行队列;
各类组合方式:
异步函数+并发队列:开启多条线程,并发执行任务
异步函数+串行队列:开启一条线程,串行执行任务
同步函数+并发队列:不开线程,串行执行任务
同步函数+串行队列:不开线程,串行执行任务
异步函数+主队列:不开线程,在主线程中串行执行任务
同步函数+主队列:不开线程,串行执行任务(发生死锁)

GCD基本函数:

 

dispatch_barrier_async/dispatch_group_notify/dispatch_semaphore_wait 不会阻塞线程
dispatch_group_wait 会阻塞线程

 

  • dispatch_barrier_async 控制任务的执行顺序

  1. queue必须是手动建立的并行队列;
  2. 全部位于dispatch_barrier_async函数以前的操做执行完毕后dispatch_barrier_async才会执行;
  3. dispatch_barrier_async函数执行以后,barrier函数以后的操做才会获得执行;
  4. 该函数须要同dispatch_queue_create函数生成的concurrent Dispatch Queue队列一块儿使用;
dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        for(int i = 0; i < 5; i++){
            NSLog(@"11111111---%@", [NSThread currentThread]);
        }
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"--dispatch_barrier_async-");
    });
    
    dispatch_async(queue, ^{
        for(int i = 0; i < 5; i++){
            NSLog(@"222222---%@", [NSThread currentThread]);
        }
    });

  执行结果:
  testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)}
  testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)}
  testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)}
  testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)}
  testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)}
  testNSThread[1563:634366] --dispatch_barrier_async-
  testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)}
  testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)}
  testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)}
  testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)}
  testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)}

 dispatch_barrier_async并不会阻塞线程,同一个并发队列中的操做等待dispatch_barrier_async执行完以后才会执行,其余操做不会等dispatch_barrier_async执行完以后再执行;多线程

dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"task1");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"task2");
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"barrier_async");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"task3");
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"task4");
    });
    
    NSLog(@"---end");

执行结果:
2017-10-12 10:16:29.368495+0800 test[5491:2328009] ---end
2017-10-12 10:16:29.370267+0800 test[5491:2328077] task1
2017-10-12 10:16:29.370539+0800 test[5491:2328733] task2
2017-10-12 10:16:29.370596+0800 test[5491:2328077] task4
2017-10-12 10:16:29.370677+0800 test[5491:2328733] barrier_async
2017-10-12 10:16:29.370795+0800 test[5491:2328733] task3

能够看到task4和输出end 不会等barrier_async执行完再执行;
  • dispatch_after

dispatch_after函数不是延迟N秒再执行block,而是在N秒以后才将block加入到队列中去; 并发

延迟时间有两种计算方式:
一、相对时间:
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC) 表示在当前时间2秒后
DISPATCH_TIME_NOW:当前时间
NSEC_PER_SEC 表示秒数
NSEC_PER_MSEC表示毫秒


dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"---%@",[NSThread currentThread]);
    });
    
二、绝对时间:绝对时间须要用 dispatch_walltime 函数建立;

dispatch_walltime(const struct timespec *_Nullable when, int64_t delta);
第一个参数是timespec结构体,
_STRUCT_TIMESPEC
{
	__darwin_time_t	tv_sec; //秒数的整数部分
	long            tv_nsec; //秒数的小数部分
};


示例代码:
    struct timespec spec = {0, 0};

    //获取当前时间作测试
    NSDate *date = [NSDate date];
    //计算当前时间到1970的秒数
    NSTimeInterval timeInterval = [date timeIntervalSince1970];
    
    timeInterval += 5;  //在当前时间延迟5秒执行
    
    double second = 0;  //秒数的整数部分
    double subSecond = 0;   //秒数的小数部分
    
    //modf函数的做用是求出整数部分和小数部分
    subSecond = modf(timeInterval, &second);
    
    spec.tv_sec = second;
    spec.tv_nsec = subSecond * NSEC_PER_SEC;
    
    dispatch_time_t time2= dispatch_walltime(&spec, 0);
    
    dispatch_after(time2, dispatch_get_global_queue(0, 0), ^{
       
        
        NSLog(@"111111");
    });
  •  dispatch_once

dispatch_once函数一般用在单例模式上,它能够保证在程序运行期间某段代码只执行一次 app

-(void)once
    {
        //整个程序运行过程当中只会执行一次
        //onceToken用来记录该部分的代码是否被执行过
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{

            NSLog(@"-----");
        });
    }
  •  dispatch_apply

  1. 把指定的block添加到queue中N次,等到全部block都执行完以后才返回;
  2.  第一个参数是迭代次数,第二个是所在的队列,第三个是当前索引;
  3. dispatch_apply能够利用多核的优点;
  4. dispatch_apply 和 dispatch_apply_f 是同步函数,会阻塞当前线程直到全部循环迭代执行完成;
  5. 当提交到并发queue时,循环迭代的执行顺序是不肯定的 ;
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
        
        NSLog(@"%zd", index);
    });

输出结果:
2017-04-27 11:13:59.439 newTest[3563:1745064] 1
2017-04-27 11:13:59.439 newTest[3563:1745019] 0
2017-04-27 11:13:59.440 newTest[3563:1745019] 3
2017-04-27 11:13:59.440 newTest[3563:1745064] 2
2017-04-27 11:13:59.441 newTest[3563:1745064] 5
2017-04-27 11:13:59.441 newTest[3563:1745019] 4
2017-04-27 11:13:59.441 newTest[3563:1745064] 6
2017-04-27 11:13:59.441 newTest[3563:1745064] 8
2017-04-27 11:13:59.441 newTest[3563:1745019] 7
2017-04-27 11:13:59.441 newTest[3563:1745064] 9
  •  dispatch_group 队列组(同栅栏函数)

下面有两种方式来实现 等待某些操做完成后才开始执行后续操做,
    方法1:

     dispatch_group_notify:当 dispatch_group中全部的block都执行完后,
     dispatch_group_notify会通知队列任务都执行完毕,并执行 dispatch_group_notify 的block;
     dispatch_group_notify不会阻塞队列;
     dispatch_group_wait : 同步的等待队列中的任务都执行完毕,才会执行后续的任务;会阻塞队列;

    dispatch_group_t group = dispatch_group_create();
    
    dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"%zd-download1--%@",i,[NSThread currentThread]);
        }
        
    });
    
    dispatch_group_async(group, queue, ^{
        
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"%zd-download2--%@",i,[NSThread currentThread]);
        }
        
    });
    
    dispatch_group_notify(group, queue, ^{
       
        NSLog(@"dispatch_group_notify");
        
    });
 
    方法2:dispatch_group_enter、dispatch_group_wait
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_enter(group);
        
    NSLog(@"group enter");
        
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for(int i = 0; i < 10; i++){
            NSLog(@"%zd----%@", i, [NSThread currentThread]);
        }
        
        NSLog(@"group leave");
        dispatch_group_leave(group);
    });
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
         NSLog(@"another task");
        
    });
须要注意的是,dispatch_group_wait实际上会使当前的线程处于等待的状态,
也就是说若是是在主线程执行dispatch_group_wait,在上面的Block执行完以前,主线程会处于卡死的状态。
能够注意到dispatch_group_wait的第二个参数是指定超时的时间,
若是指定为DISPATCH_TIME_FOREVER(如上面这个例子)则表示会永久等待,直到上面的Block所有执行完,
除此以外,还能够指定为具体的等待时间,根据dispatch_group_wait的返回值来判断是上面block执行完了仍是等待超时了。
最后,同以前建立dispatch_queue同样,若是是在OS X 10.8或iOS 6以及以后版本中使用,
Dispatch Group将会由ARC自动管理,若是是在此以前的版本,须要本身手动释放。
  • Dispatch Semaphore

信号量在多线程开发中被普遍使用,当一个线程在进入一段关键代码以前,线程必须获取一个信号量,一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待前面的线程释放信号量。
信号量的具体作法是:当信号计数大于0时,每条进来的线程使计数减1,直到变为0,变为0后其余的线程将进不来,处于等待状态;执行完任务的线程释放信号,使计数加1,如此循环下去。异步

下面这个例子中使用了10条线程,可是同时只执行一条,其余的线程处于等待状态:

//dispatch_semaphore_wait :减小信号量计数.若是结果小于0, 等待信号唤醒.
//dispatch_semaphore_signal : 增长信号量计数,若是以前的值小于0 唤醒 dispatch_semaphore_wait
    
    //第一步:初始信号量= 0
    semaphore = dispatch_semaphore_create(0);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
        for(NSInteger i = 0; i < 10; i++){
            NSLog(@"--------------%zd", i);
        }
        
        //第三步:任务执行完 判断信号量小于0  唤醒dispatch_semaphore_wait
        dispatch_semaphore_signal(semaphore);
    });
    
    //第二步:信号量减1 变成 -1,小于0 等待唤醒
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    NSLog(@"dddddddddddddddddd");
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        
    });


取得信号量的线程在2秒后释放了信息量,至关因而每2秒执行一次。
经过上面的例子能够看到,在GCD中,用dispatch_semaphore_create函数能初始化一个信号量,同时须要指定信号量的初始值;
使用dispatch_semaphore_wait函数分配信号量并使计数减1,为0时处于等待状态;
使用dispatch_semaphore_signal函数释放信号量,并使计数加1。
另外dispatch_semaphore_wait一样也支持超时,只须要给其第二个参数指定超时的时候便可,
同Dispatch Group的dispatch_group_wait函数相似,能够经过返回值来判断。
这个函数也须要注意,若是是在OS X 10.8或iOS 6以及以后版本中使用,Dispatch Semaphore将会由ARC自动管理,
若是是在此以前的版本,须要本身手动释放。
相关文章
相关标签/搜索