GCD(Grand Center Dispatch)使用详解
来源:互联网 发布:淘宝上可以卖电子书吗 编辑:程序博客网 时间:2024/06/14 22:39
一、GCD简介
GCD是苹果公司推出的基于多核CPU多线程技术,GCD属于系统级的线程管理,在Dispatch queue中执行需要执行的任务性能非常高。GCD这块已经开源,地址是 http://libdispatch.macosforge.org。
二、按获取方式分类队列
dispatch queue队列就是执行任务的队列,由于遵循 First in First out(先进先出),所以又称为 FIFO队列。队列有一个基本概念是串行队列和并发队列。
GCD中按获取方式划分:
1. 公开现成队列
公开现成队列是直接可以获取,不需要创建,而且开发者也不能够创建该队列。
公开现成队列又分为5种,分别是主队列、四种通用调度队列,主队列就是运行在主线程上的队列,四种通用调度队列分别如下:
QOS_CLASS_USER_INTERACTIVE: user interactive等级表示任务需要被立即执行提供好的体验,用来更新UI,响应事件等。这个等级最好保持小规模。
QOS_CLASS_USER_INITIATED:user initiated等级表示任务由用户发起异步执行。适用场景是需要及时得到结果同时又可以继续交互的时候。
QOS_CLASS_UTILITY:utility等级表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务需要节能。
QOS_CLASS_BACKGROUND:background等级表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。
示例: 后台加载显示图片:
func loadRemoteImage(URLString: String){ dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.rawValue), 0)) { let image = self.downloadImageFromURL(URLString) dispatch_async(dispatch_get_main_queue()) { // 图片完成,把一个闭包提交到主线程上更新UI self.easeInNewImage(image) // 更新 UI } } }
1.1 运行到主线程上的main queue
dispatch_get_main_queue()
1.2 High Priority Queue
dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.rawValue), 0)
1.3 Default Priority Queue
dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.rawValue), 0)
1.4 Low Priority Queue
dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.rawValue), 0)
1.5 Background Priority Queue(用于I / O)
dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.rawValue), 0)
可以使用下面的方法简化QoS等级参数的写法:
var GlobalMainQueue: dispatch_queue_t { return dispatch_get_main_queue()}var GlobalUserInteractiveQueue: dispatch_queue_t { return dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.value), 0)}var GlobalUserInitiatedQueue: dispatch_queue_t { return dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)}var GlobalUtilityQueue: dispatch_queue_t { return dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0)}var GlobalBackgroundQueue: dispatch_queue_t { return dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0)}//使用起来就是这样,易读而且容易看出在使用哪个队列dispatch_async(GlobalUserInitiatedQueue) { let overlayImage = self.faceOverlayImageFromImage(self.image) dispatch_async(GlobalMainQueue) { self.fadeInNewImage(overlayImage) }}
2 . 自定义队列。
自定义队列有两种类型:串行队列、并发队列
串行队列:底层维护了一个线程池,线程池中维护了一个线程,提交任务到该类队列上时,只能提交到一个线程上,不能实现多线程编程;
dispatch_queue_create("com.manchelle.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL)
并发队列:底层维护了一个线程池,线程池中维护了多个线程,提交任务到该类队列上时,任务由系统自动配置多个线程上,能实现多线程编程。
dispatch_queue_create("com.manchelle.gcddemo.concurrent", DISPATCH_QUEUE_CONCURRENT)
五、自定义队列优先级设置方式
自定义队列优先级:可以通过dispatch_queue_attr_make_with_qos_class或者dispatch_set_target_queue方法设置队列优先级
/** dipatch_queue_attr_make_with_qos_class 方式 * QOS_CLASS_USER_INITIATED** QOS_CLASS_USER_INTERACTIVE* * QOS_CLASS_UTILITY* * QOS_CLASS_BACKGROUND*/dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, -1);dispatch_queue_t queue = dispatch_queue_create("com.manchelle.gcddemo.qosqueue", attr);
/** dispatch_set_target_queue ****/dispatch_queue_t queue = dispatch_queue_create("com.manchelle.gcddemo.settargetqueue",DISPATCH_QUEUE_CONCURRENT); //需要设置优先级的queuedispatch_queue_t referQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //参考优先级dispatch_set_target_queue(queue, referQueue); //设置queue和referQueue的优先级一样
七、各种队列使用时机
主队列(顺序):队列中有任务完成需要更新UI时,dispatch_after在这种类型中使用。
并发队列:用来执行与UI无关的后台任务,dispatch_sync放在这里,方便等待任务完成进行后续处理或和dispatch barrier同步。dispatch groups放在这里也不错。
自定义顺序队列:顺序执行后台任务并追踪它时。这样做同时只有一个任务在执行可以防止资源竞争。dipatch barriers解决读写锁问题的放在这里处理。dispatch groups也是放在这里。
八、dispatch_groups用法
dispatch groups 是专门用来监视多个异步任务,dispatch_group_t实例用来追踪不同队列中的不同任务。
当group里所有事件都执行完成, GCD API 有两种方式发送通知。第一种是dispatch_group_wait,这种方式会阻塞当前线程,等所有任务都完成或者等待超时;第二种方式是使用dispatch_group_notify,异步执行闭包,不会阻塞当前线程。
第一种使用dispatch_group_wait的Swift例子:
func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) { dispatch_async(GlobalUserInitiatedQueue) { // 因为dispatch_group_wait会租塞当前进程,所以要使用dispatch_async将整个方法要放到后台队列才能够保证主线程不被阻塞 var storedError: NSError! var downloadGroup = dispatch_group_create() // 创建一个dispatch group for address in [OverlyAttachedGirlfriendURLString, SuccessKidURLString, LotsOfFacesURLString] { let url = NSURL(string: address) dispatch_group_enter(downloadGroup) // dispatch_group_enter是通知dispatch group任务开始了,dispatch_group_enter和dispatch_group_leave是成对调用,不然程序就崩溃了。 let photo = DownloadPhoto(url: url!) { image, error in if let error = error { storedError = error } dispatch_group_leave(downloadGroup) // 保持和dispatch_group_enter配对。通知任务已经完成 } PhotoManager.sharedManager.addPhoto(photo) } dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER) // dispatch_group_wait等待所有任务都完成直到超时。如果任务完成前就超时了,函数会返回一个非零值,可以通过返回值判断是否超时。也可以用DISPATCH_TIME_FOREVER表示一直等。 dispatch_async(GlobalMainQueue) { // 这里可以保证所有图片任务都完成,然后在main queue里加入完成后要处理的闭包,会在main queue里执行。 if let completion = completion { // 执行闭包内容 completion(error: storedError) } } }}
Objective-C例子:
- (void)dispatchGroupWaitDemo { dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); //在group中添加队列的block dispatch_group_async(group, concurrentQueue, ^{ [NSThread sleepForTimeInterval:2.f]; NSLog(@"1"); }); dispatch_group_async(group, concurrentQueue, ^{ NSLog(@"2"); }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); NSLog(@"go on");}
第二种使用dispatch_group_notify的Swift例子:
func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) { // 不用加dispatch_async,因为没有阻塞主进程 var storedError: NSError! var downloadGroup = dispatch_group_create() for address in [OverlyAttachedGirlfriendURLString, SuccessKidURLString, LotsOfFacesURLString] { let url = NSURL(string: address) dispatch_group_enter(downloadGroup) let photo = DownloadPhoto(url: url!) { image, error in if let error = error { storedError = error } dispatch_group_leave(downloadGroup) } PhotoManager.sharedManager.addPhoto(photo) } dispatch_group_notify(downloadGroup, GlobalMainQueue) { // dispatch_group_notify和dispatch_group_wait的区别就是是异步执行闭包的,当dispatch groups中没有剩余的任务时闭包才执行。这里是指明在主队列中执行。 if let completion = completion { completion(error: storedError) } }}
Objective-C例子:
//dispatch_group_notify- (void)dispatchGroupNotifyDemo { dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, concurrentQueue, ^{ NSLog(@"1"); }); dispatch_group_async(group, concurrentQueue, ^{ NSLog(@"2"); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"end"); }); NSLog(@"can continue");}
对现有API 扩展dispatch_group_t :
// NSURLSession 例子extension NSURLSession { public func withGroup(group: dispatch_group_t?, request: NSURLRequest, completionHandler:(NSData?, NSURLResponse?, NSError?) -> Void) { if group == nil { self.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in completionHandler(data,response,error) }) }else { dispatch_group_enter(group!) self.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in completionHandler(data,response,error) dispatch_group_leave(group!) }) } }}
//给Core Data的-performBlock:添加groups。组合完成任务后使用dispatch_group_notify来运行一个block即可。- (void)withGroup:(dispatch_group_t)group performBlock:(dispatch_block_t)block{ if (group == NULL) { [self performBlock:block]; } else { dispatch_group_enter(group); [self performBlock:^(){ block(); dispatch_group_leave(group); }]; }}
Note:
dispatch_group_enter与dispatch_group_leave的组合等价于dispatch_group_async;
dispatch_group_enter与dispatch_group_leave必须成对出现。
九、dispatch_after用法
dispatch_after 只是延后提交block,并不是延时执行
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC))) dispatch_after(time, GlobalMainQueue, { () -> Void in })
// 解释一下上面参数dispatch_time(argv1,argv2)第一个参数: DISPATCH_TIME_NOW 描述从现在开始计时;第二个参数: 延时时间,单位为纳秒,所以秒数要乘以一秒的毫秒数;系统提供了几个宏定义:NSEC_PER_MSEC: 一毫秒多少纳秒NSEC_PER_SEC: 一秒多少纳秒NSEC_PER_USEC: 一微秒多少纳秒
十、dispatch_barrier_async 用法
Dispatch_barrier_async确保提交的闭包是指定队列中特定时段唯一在执行的一个。在所有先于Dispatch_barrier_async提交的任务都完成的情况下这个闭包才会执行。轮到这个闭包执行的时候,该队列其他任务将不会被执行,执行完之后,队列恢复。需要注意的是Dispatch_Barrier_async只能用到自己创建的并发队列上,全局并发队列和串行队列并不起作用,效果和dispatch_sync一样。
/** * dispatch_Barrier_async 简单模拟读写操作结构 * */ func dispatchBarrierAsyncDemo(){ // 为防止读写冲突,可以创建一个并发队列,没有数据更新的只读操作可以异步执行,而写操作时必须保证其他操作不能同时进行。 let dataQueue = dispatch_queue_create("com.manchelle.gcddemo.dispatch_barrier", DISPATCH_QUEUE_CONCURRENT) dispatch_async(dataQueue) { print("read data!") NSThread.sleepForTimeInterval(1.0) } dispatch_async(dataQueue) { print("read data!") NSThread.sleepForTimeInterval(2.0) } // 等前面的任务执行完成之后才能单独执行barrier中的任务,而且barrier后面的也不能执行。 dispatch_barrier_async(dataQueue) { print("write data!") NSThread.sleepForTimeInterval(3.0) } dispatch_async(dataQueue) { print("read data!") NSThread.sleepForTimeInterval(4.0) } dispatch_async(dataQueue) { print("read data!") NSThread.sleepForTimeInterval(5.0) } }
// 解决读写操作的代码框架 var _newsArray:[New] = [] private let concurrentQueue = dispatch_queue_create("com.manchelle.gcddemo.dispatch_barrier", DISPATCH_QUEUE_CONCURRENT) // 解决写死锁 func addNew(new: New) { dispatch_barrier_async(concurrentQueue) { // 写操作,为防止多个线程同时写操作造成死锁,使用barrier在同一时刻只允许这一个任务执行。 self._newsArray.append(new) dispatch_async(dispatch_get_main_queue()) { // 更新UI } } } // 上面解决了写操作可能发生死锁,下面使用dispatch_sync解决读时可能发生死锁 var newsArray:[New] { var newsCopy:[New]! dispatch_sync(concurrentQueue) { newsCopy = self._newsArray } return newsCopy }
都用异步处理避免死锁,异步的缺点在于调试不方便,但是比起同步容易产生死锁这个副作用还算小的。
十一、dispatch_once用法
dispatch_once_t要是全局或static变量,保证dispatch_once_t只有一份实例
+ (UIColor *)boringColor;{ static UIColor *color; //只运行一次 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ color = [UIColor colorWithRed:0.380f green:0.376f blue:0.376f alpha:1.000f]; }); return color;}
十二、dispatch_async用法
设计一个异步的API,需要条用dispatch_async(),这个调用放在API的方法或函数中做。让使用者设置一个回调的处理队列
- (void)processImage:(UIImage *)image completionHandler:(void(^)(BOOL success))handler;{ dispatch_async(self.isolationQueue, ^(void){ // do actual processing here dispatch_async(self.resultQueue, ^(void){ handler(YES); }); });}
执行一些比较耗时的操作,且易卡住主线程的,比如读取网络数据,大数据IO,还有大量数据数据库读写。这是需要另起线程,然后通知主线程更新界面。GCD使用起来比NSOperation与NSThread方便简单许多。
// 代码框架 func frameDemo() { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)){ // 耗时的操作 dispatch_async(dispatch_get_main_queue(), { () -> Void in // 更新UI }) } } // 下载图片示例 func downloadImageDemo() { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)){ let URL = NSURL(string: "http://avatar.csdn.net......") let data: NSData? = NSData(contentsOfURL: URL!) dispatch_async(dispatch_get_main_queue(), { () -> Void in if let tempData = data { self.imageView.image = UIImage(data: tempData) } }) } }
十三、dispatch_apply用法
类似for循环,但是在并发队列的情况下,并发执行block
for (size_t y = 0; y < height; ++y) { for (size_t x = 0; x < width; ++x) { // Do something with x and y here }}// 因为可以并发执行,所以使用dispatch_apply可以运行更快let concurrentQueue = dispatch_queue_create("com.manchelle", DISPATCH_QUEUE_CONCURRENT) dispatch_apply(10, concurrentQueue) { (i) -> Void in print("\(i)") } print("apply end") // dispatch_apply会阻塞线程,所以要等上诉block执行完成之后才会执行这句
dispatch_apply能避免线程爆炸,因为GCD会管理并发
func dealWithThreadWithMaybeExplode(explode: Bool) { if(explode) { //有问题的情况,可能会死锁 for (var i = 0; i < 999 ; i++) { dispatch_async(dispatch_queue_create("com.manchell.gcddemo", DISPATCH_QUEUE_CONCURRENT)) { print("wrong == \(i)") } } } else { //会优化很多,能够利用GCD管理 dispatch_apply(999, dispatch_queue_create("com.manchell.gcddemo", DISPATCH_QUEUE_CONCURRENT), { (n) -> Void in print("wrong == \(n)") }) } }
// 示例:func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) { var storedError: NSError! var downloadGroup = dispatch_group_create() let addresses = [OverlyAttachedGirlfriendURLString, SuccessKidURLString, LotsOfFacesURLString] dispatch_apply(UInt(addresses.count), GlobalUserInitiatedQueue) { i in let index = Int(i) let address = addresses[index] let url = NSURL(string: address) dispatch_group_enter(downloadGroup) let photo = DownloadPhoto(url: url!) { image, error in if let error = error { storedError = error } dispatch_group_leave(downloadGroup) } PhotoManager.sharedManager.addPhoto(photo) } dispatch_group_notify(downloadGroup, GlobalMainQueue) { if let completion = completion { completion(error: storedError) } }}
十四、dispatch_block_t用法
十五、dispatch_block_t在任务执行前进行取消
十六、dispatch_io_t用法
十七、dispatch_source用法
十八、dispatch_semaphore用法
十九、dispatch_suspend和dispatch_resume用法
二十、dispatch_set_context和dispatch_get_context
二十一、GCD死锁
二十二、GCD实际使用
二十三iOS系统版本新特性
- GCD(Grand Center Dispatch)使用详解
- 关于GCD(Grand Center Dispatch)
- Grand Central Dispatch(GCD)详解
- Grand Central Dispatch(GCD)详解
- iOS Grand Central Dispatch (GCD) 的一般使用详解
- Grand Central Dispatch(GCD)介绍和使用
- Grand Central Dispatch(GCD)介绍和使用
- Grand Central Dispatch (GCD)
- GCD(Grand Central Dispatch)
- Grand Central Dispatch(GCD)
- Grand Central Dispatch (GCD
- Grand Central Dispatch (GCD)
- GCD(Grand Central Dispatch)
- GCD(Grand Central Dispatch)
- GCD(Grand Central Dispatch)
- GCD (Grand Central Dispatch)
- GCD(Grand Central Dispatch)
- Grand Central Dispatch(GCD)
- 磁盘的驱动调度如何实现公平、高效的访盘请求?
- ViewPager关于PagerAdapter详解
- 同一个脚本在SQLPLUS和SQLDEV上的不同
- 坑爹的工行一代U盾
- 有向无环词图(DAWG)
- GCD(Grand Center Dispatch)使用详解
- Mysql优化建议
- 数组
- linux network command
- 3.单例模式
- 【Java】官网中方法的值传递和引用传递
- 矩阵快速幂
- C++中使用空对象指针调用成员函数
- CNTK从入门到深入研究(4) - SGD随机梯度下降法