GCD两个基本知识:任务和队列;
任务执行方式分为两种:同步(sync)和异步(async) 队列也分为两种:并发队列和串行队列; 各类组合方式: 异步函数+并发队列:开启多条线程,并发执行任务 异步函数+串行队列:开启一条线程,串行执行任务 同步函数+并发队列:不开线程,串行执行任务 同步函数+串行队列:不开线程,串行执行任务 异步函数+主队列:不开线程,在主线程中串行执行任务 同步函数+主队列:不开线程,串行执行任务(发生死锁)
GCD基本函数:
dispatch_barrier_async/dispatch_group_notify/dispatch_semaphore_wait 不会阻塞线程 dispatch_group_wait 会阻塞线程
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函数不是延迟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函数一般用在单例模式上,它能够保证在程序运行期间某段代码只执行一次 app
-(void)once { //整个程序运行过程当中只会执行一次 //onceToken用来记录该部分的代码是否被执行过 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"-----"); }); }
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
下面有两种方式来实现 等待某些操做完成后才开始执行后续操做, 方法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自动管理,若是是在此以前的版本,须要本身手动释放。
信号量在多线程开发中被普遍使用,当一个线程在进入一段关键代码以前,线程必须获取一个信号量,一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待前面的线程释放信号量。
信号量的具体作法是:当信号计数大于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自动管理, 若是是在此以前的版本,须要本身手动释放。