【《Objective-C 高级编程》 学习笔记--GCD】
2015-09-02 22:35
447 查看
GCD :Grand Central Dispatch 是异步执行任务的技术之一。开发者只需要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。
在引入GCD之前,cocoa框架提供了NSObject类的performSelectorInBackground:withObject实例方法和performSelectorOnMainThread实例方法等简单的多线程编程技术。例如可以改用performSelector系方法来实现前面使用的GCD的源代码:
线程是操作系统调度的最小调度了,单线程可以理解为cup执行的一条无分叉的路径(程序序列,程序最后都是编译成Cpu命令来执行的),多线程就是多条线程同时执行。但是每条线程不会出现分支。。多核的情况下很好理解,对线程 即一个核执行一个线程达成多线程,而单核可以虚拟成多核来进行多线程操作(尽管计算机程序是在宏观上是并行 微观上串行的;在线程见切换的时候,CPU寄存器信息等信息会保存前一个线程对应的内存块中即保存上下文,它们之间的切换也称为上下文切换)。
当然多线程操作容易导致很多问题,如死锁,或者线程开启太多消耗太多导致消耗大量内存。尽管会出现这些问题,但是使用多线程能保证应用程序的响应性能。
应用程序在启动时,通过最先执行的线程,即“主线程”来描述用户界面,处理触摸事件等。如果某个操作需要进行长时间的处理那么就需要对该操作开启另一个线程去执行这个操作,而不是挂在主线程上。
CGD的API
开发者要做的只是定义想执行的任务并追加到适当的Dispatch Queue中。
对应源码如下:
存在两种Dispatch Queue
1 Serial Dispatch Queue (相当于同步,等待执行中处理结束,才开始执行下一个任务)
2 Concurrent Dispathc Queue(相当于异步,不用等待现在执行中的处理结束)
(当然同步异步还有消息响应方面的知识,同发送消息后必须等待消息处理完成才继续运行,而异步任务发出消息后不用等待发送的消息处理结果而是继续运行)
得到这两种队列有两种方式:
创建队列的第一种方式:
通过dispatch_queue_create函数可以生成Dispatch Queue:
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue",NULL);
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.example.gcd.MyConcurrentDispatchQueue",DISPATCH_QUEUE_CONCURRENT );
其中第一个参数是只得队列名称,使用逆域名的方式,第二个参数值得就是队列类型,为NULL只得就是Serial Dispatch Queue ,为DISPATCH_QUEUE_CONCURRENT 只得是Concurrent Dispatch Queue。
使用dispatch_queue_create可以创建任意多个队列(但是Serial Dispatch Queue 和Concurrent Dispatch Queue受到Cpu限制),但是创建过多的线程会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能。
为了避免多线程问题(多线程更新相同资源导致数据竞争) 可以使用Serial Dispatch Queue。
值得注意的是,Dispatch Queue并没有自动释放的能力,即使是开启了ARC, 还是需要程序员手动释放。
dispatch_queue_create生成的Dispatch Queue在使用结束后需要手动释放,使用函数:dispatch_release
如:
dispatch_release(mySerialDispatch_Queue);
由该函数名中的release可以推论出:dispatch_retain(mySerialDispatch_Queue);
Dispatch Queue也像Objective-C的引用计数式内存管理一样。
创建队列的第二种方式:
第二种方法是获取系统标准提供的Dispatch Queue。
其实不用特意生成Dispatch Queue,系统会自动给我们生成两个:Main Dispatch Queue和Global Dispatch Queue。
Main Dispatch 是在主线程中执行的Dispatch Queue,因为主线程只有一个,所以Main Dispatch Queue自然就是Serial Dispatch Queue。追加到Main Dispatch Queue的处理在主线程的RunLoop中处理。
Dispatch Queue的种类
Diaptch Queue的获取方式如下:
另外,对于Main Dispatch Queue 和Global Dispatch Queue执行dispatch_retain函数和dispatch_release函数不会引
dc32
起任何变化,也不会有任何问题。也是获取并使用Global Dispatch Queue比生成、使用、释放Concurrent Dispatch Queue更轻松的原因。
变更Dispatch Queue的优先级:dispatch_set_target_queue
使用dispatch_queue_create函数生成的Dispatch Queue(两种:Serial 和Concurrent)可以使用dispatch_set_target_queue变更Dispatch的执行优先级(只针对dispatch_queue_create产生的队列),在后台处理Serial Dispatch Queue如下:
在必须将不可并行执行的处理追加到多个Serial Dispatch Queue中时,如果使用dispatch_set_target_queue函数将目标指定为某一个Serial Dispatch Queue,既可以防止处理并行执行。
指定时间后执行处理可使用:dispatch_after函数
在3秒后指定Block追加到Main Dispatch Queue中,源码如下:
注意dispatch_after并不是指定时间后处理,而是在指定时间追加处理到Dispatch Queue ,上述源码在3秒后追加Block 到Main Dispatch Queue。
dispatch_time用于计算相对时间,而dispatch_walltime函数用于计算绝对时间(从1970年开始)。
注:NSEC_PER_SEC 宏应该是代表一秒钟包含多少纳秒, NSEC_PER_MSEC 宏应该是代表一毫秒包含多少纳秒
参考:http://www.tuicool.com/articles/zaUziue
Dispatch Group
在追加到Dispatch Queue中的多个处理全部结束后,希望执行结束处理,就可以使用Dispatch Group (当然是用一个Serial Dispatch Queue,但如果使用Concurrent Dispatch Queue 或使用多个Dispatch Queue时 则需要使用dispatch group)。
结果如下:
blk1
blk2
blk0
done
注:几个block的执行顺序不定,但是done一定是最后输出。在追加到Dispatch Group 中的处理全部结束时,该源代码中使用的dispatch_group_notify函数会将执行的Block追加到Dispatch Queue中,将第一个参数指定为要件事的Dispatch Queue。
Dispatch中也可以使用dispatch_group_wait函数 仅等待全部处理执行结束。
dispatch_group_wait(group ,DISPATCH_TIME_FOREVER);
其中DISPATCH_TIME_FOREVER 指定等待时间,它属于dispatch_time_t类型的值。该源代码使用DISPATCH_TIME_FOREVER 意味着永久等待。这里的等待指的是一旦调用dispatch_group_wait函数,该函数就处于调用的状态而不放回。即执行dispatch_group_wait函数的现在的线程(当前线程)停止。在经过dispatch_group_wait函数中指定的时间或属于指定dispatch
group 的处理全部执行结束之前,执行该函数的线程停止。
指定DISPATCH_TIME_NOW 则不用任何等待即可以判定属于Dispatch Group的里是否执行结束。
dispatch_async(queue, ^{ /*** 长时间处理 如AR用画像识别/数据库访问 **/ /* 长时间处理结束,主线程使用该处理结果 */ dispatch_asyc(dispatch_get_main_queue(), ^{ /* 只是主线程可以执行的处理 例如 用户界面更新 */ }) }) |
/* NSObject performSelectorInBackground:withObject:方法中 执行后台线程 */ - (void) launchThreadByNSObject_performSelectorInBackground_withOObject { [self performSelectorInBackground:@selector(dowork)] withObject:nil]; } /* 后台线程处理方法 */ - (void) doWork { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init]; /* 长时间处理 如AR用画像识别/数据库访问 */ /* 长时间处理结束,主线程使用该处理结果 */ [self performSelectorOnMainThread:@selector(doneWork) withObject:nil waitUntilDone:NO]; [pool drain]; } - (void) doneWork { /* 只在主线程可以执行的处理 例如 用户界面更新 */ } |
当然多线程操作容易导致很多问题,如死锁,或者线程开启太多消耗太多导致消耗大量内存。尽管会出现这些问题,但是使用多线程能保证应用程序的响应性能。
应用程序在启动时,通过最先执行的线程,即“主线程”来描述用户界面,处理触摸事件等。如果某个操作需要进行长时间的处理那么就需要对该操作开启另一个线程去执行这个操作,而不是挂在主线程上。
CGD的API
开发者要做的只是定义想执行的任务并追加到适当的Dispatch Queue中。
对应源码如下:
dispatch_async(queue, ^{ /*想执行的任务*/ }) |
1 Serial Dispatch Queue (相当于同步,等待执行中处理结束,才开始执行下一个任务)
2 Concurrent Dispathc Queue(相当于异步,不用等待现在执行中的处理结束)
(当然同步异步还有消息响应方面的知识,同发送消息后必须等待消息处理完成才继续运行,而异步任务发出消息后不用等待发送的消息处理结果而是继续运行)
得到这两种队列有两种方式:
创建队列的第一种方式:
通过dispatch_queue_create函数可以生成Dispatch Queue:
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue",NULL);
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.example.gcd.MyConcurrentDispatchQueue",DISPATCH_QUEUE_CONCURRENT );
其中第一个参数是只得队列名称,使用逆域名的方式,第二个参数值得就是队列类型,为NULL只得就是Serial Dispatch Queue ,为DISPATCH_QUEUE_CONCURRENT 只得是Concurrent Dispatch Queue。
使用dispatch_queue_create可以创建任意多个队列(但是Serial Dispatch Queue 和Concurrent Dispatch Queue受到Cpu限制),但是创建过多的线程会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能。
为了避免多线程问题(多线程更新相同资源导致数据竞争) 可以使用Serial Dispatch Queue。
值得注意的是,Dispatch Queue并没有自动释放的能力,即使是开启了ARC, 还是需要程序员手动释放。
dispatch_queue_create生成的Dispatch Queue在使用结束后需要手动释放,使用函数:dispatch_release
如:
dispatch_release(mySerialDispatch_Queue);
由该函数名中的release可以推论出:dispatch_retain(mySerialDispatch_Queue);
Dispatch Queue也像Objective-C的引用计数式内存管理一样。
创建队列的第二种方式:
第二种方法是获取系统标准提供的Dispatch Queue。
其实不用特意生成Dispatch Queue,系统会自动给我们生成两个:Main Dispatch Queue和Global Dispatch Queue。
Main Dispatch 是在主线程中执行的Dispatch Queue,因为主线程只有一个,所以Main Dispatch Queue自然就是Serial Dispatch Queue。追加到Main Dispatch Queue的处理在主线程的RunLoop中处理。
Dispatch Queue的种类
名称 | Dispatch Queue的种类 | 说明 |
Main Dispatch Queue | Serial Dispatch Queue | 主线程执行 |
Global Dispatch Queue(high Priority) | Concurrent Dispatch Queue | 执行优先级:高(最高优先级) |
Global Dispatch Queue(Default Priority) | Concurrent Dispatch Queue | 执行优先级:默认 |
Global Dispatch Queue(Low Priority) | Concurrent Dispatch Queue | 执行优先级:低 |
Global Dispatch Queue(Backgroud Priority) | Concurrent Dispatch Queue | 执行优先级:后台 |
//Main Dispatch Queue的获取方法 dispatch_queue_t mainDispatchQueue=dispatch_get_main_queue( ); //Global Dispatch Queue 的获取方法,高优先级 dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0) //同理对应其他优先级的参数如下: DISPATCH_QUEUE_PRIORITY_DEFAULT DISPATCH_QUEUE_PRIORITY_LOW DISPATCH_QUEUE_PRIORITY_BACKGROUND |
dc32
起任何变化,也不会有任何问题。也是获取并使用Global Dispatch Queue比生成、使用、释放Concurrent Dispatch Queue更轻松的原因。
变更Dispatch Queue的优先级:dispatch_set_target_queue
使用dispatch_queue_create函数生成的Dispatch Queue(两种:Serial 和Concurrent)可以使用dispatch_set_target_queue变更Dispatch的执行优先级(只针对dispatch_queue_create产生的队列),在后台处理Serial Dispatch Queue如下:
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create('com.example,gcd.myserialDispatchQueue',NULL); dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ); dispatch_set_target_queue(mySerialDispatchQueue , globalDispatchQueueBackground ); |
指定时间后执行处理可使用:dispatch_after函数
在3秒后指定Block追加到Main Dispatch Queue中,源码如下:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull* NESC_PER_SEC); dispatch_after(time,dispatch_get_main_queue(),^{ NSLog(@"waited at least three second . "); }); |
dispatch_time用于计算相对时间,而dispatch_walltime函数用于计算绝对时间(从1970年开始)。
注:NSEC_PER_SEC 宏应该是代表一秒钟包含多少纳秒, NSEC_PER_MSEC 宏应该是代表一毫秒包含多少纳秒
参考:http://www.tuicool.com/articles/zaUziue
Dispatch Group
在追加到Dispatch Queue中的多个处理全部结束后,希望执行结束处理,就可以使用Dispatch Group (当然是用一个Serial Dispatch Queue,但如果使用Concurrent Dispatch Queue 或使用多个Dispatch Queue时 则需要使用dispatch group)。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFUALT,0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async( group, queue,^{NSLog(@"blk0"); }); dispatch_group_async( group, queue,^{NSLog(@"blk2"); }); dispatch_group_async( group, queue,^{NSLog(@"blk3"); }); dispatch_group_notify(group, dispatch_get_main_queue(),^{NSLog(@"done");}); dispatch_release(group); |
blk1
blk2
blk0
done
注:几个block的执行顺序不定,但是done一定是最后输出。在追加到Dispatch Group 中的处理全部结束时,该源代码中使用的dispatch_group_notify函数会将执行的Block追加到Dispatch Queue中,将第一个参数指定为要件事的Dispatch Queue。
Dispatch中也可以使用dispatch_group_wait函数 仅等待全部处理执行结束。
dispatch_group_wait(group ,DISPATCH_TIME_FOREVER);
其中DISPATCH_TIME_FOREVER 指定等待时间,它属于dispatch_time_t类型的值。该源代码使用DISPATCH_TIME_FOREVER 意味着永久等待。这里的等待指的是一旦调用dispatch_group_wait函数,该函数就处于调用的状态而不放回。即执行dispatch_group_wait函数的现在的线程(当前线程)停止。在经过dispatch_group_wait函数中指定的时间或属于指定dispatch
group 的处理全部执行结束之前,执行该函数的线程停止。
指定DISPATCH_TIME_NOW 则不用任何等待即可以判定属于Dispatch Group的里是否执行结束。
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- ruby实现的一个异步文件下载HttpServer实例
- C#线程间不能调用剪切板的解决方法
- 科学知识:同步、异步、阻塞和非阻塞区别
- 探讨Ajax中同步与异步之间的区别
- C#线程同步的三类情景分析
- C#获取进程或线程相关信息的方法
- C#停止线程的方法
- C#子线程更新UI控件的方法实例总结
- C#线程队列用法实例分析
- C#中异步回调函数用法实例
- C++使用CriticalSection实现线程同步实例
- 基于C++实现的线程休眠代码
- C#实现异步GET的方法