iOS GCD使用指南 数组
版权声明:如需转载,请注明出处,谢谢! 安全
http://blog.csdn.net/zhangao0086/article/details/38904923 多线程
Grand Central Dispatch(GCD)是异步执行任务的技术之一。通常将应用程序中记述的线程管理用的代码在系统级中实现。开发者只须要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。因为线程管理是做为系统的一部分来实现的,所以可统一管理,也可执行任务,这样就比之前的线程更有效率。 并发
Dispatch Queue app
Dispatch Queue是用来执行任务的队列,是GCD中最基本的元素之一。 异步
Dispatch Queue分为两种: async
• Serial Dispatch Queue,按添加进队列的顺序(先进先出)一个接一个的执行 函数
• Concurrent Dispatch Queue,并发执行队列里的任务 spa
简而言之,Serial Dispatch Queue只使用了一个线程,Concurrent Dispatch Queue使用了多个线程(具体使用了多少个,由系统决定)。 .net
能够经过两种方式来得到Dispatch Queue,第一种方式是本身建立一个:
let myQueue: dispatch_queue_t = dispatch_queue_create("com.xxx", nil)
第一个参数是队列的名称,通常是使用倒序的全域名。虽然能够不给队列指定一个名称,可是有名称的队列可让咱们在遇到问题时更好调试;当第二个参数为nil时返回Serial Dispatch Queue,如上面那个例子,当指定为DISPATCH_QUEUE_CONCURRENT时返回Concurrent Dispatch Queue。
须要注意一点,若是是在OS X 10.8或iOS 6以及以后版本中使用,Dispatch Queue将会由ARC自动管理,若是是在此以前的版本,须要本身手动释放,以下:
let myQueue: dispatch_queue_t = dispatch_queue_create("com.xxx", nil)
dispatch_async(myQueue, { () -> Void in
println("in Block")
})
dispatch_release(myQueue) //释放线程
以上是经过手动建立的方式来获取Dispatch Queue,第二种方式是直接获取系统提供的Dispatch Queue。
要获取的Dispatch Queue无非就是两种类型:
• Main Dispatch Queue
• Global Dispatch Queue / Concurrent Dispatch Queue
通常只在须要更新UI时咱们才获取Main Dispatch Queue,其余状况下用Global Dispatch Queue就知足需求了:
//获取Main Dispatch Queue
let mainQueue = dispatch_get_main_queue()
//获取Global Dispatch Queue
let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
获得的Global Dispatch Queue其实是一个Concurrent Dispatch Queue,Main Dispatch Queue实际上就是Serial Dispatch Queue(而且只有一个)。
获取Global Dispatch Queue的时候能够指定优先级,能够根据本身的实际状况来决定使用哪一种优先级。
通常状况下,咱们经过第二种方式获取Dispatch Queue就好了。
dispatch_after
dispatch_after能让咱们添加进队列的任务延时执行,好比想让一个Block在10秒后执行:
var time = dispatch_time(DISPATCH_TIME_NOW, (Int64)(10 * NSEC_PER_SEC))
dispatch_after(time, globalQueue) { () -> Void in
println("在10秒后执行")
}
NSEC_PER_SEC表示的是秒数,它还提供了NSEC_PER_MSEC表示毫秒。
上面这句dispatch_after的真正含义是在10秒后把任务添加进队列中,并非表示在10秒后执行,大部分状况该函数能达到咱们的预期,只有在对时间要求很是精准的状况下才可能会出现问题。
获取一个dispatch_time_t类型的值能够经过两种方式来获取,以上是第一种方式,即经过dispatch_time函数,另外一种是经过dispatch_walltime函数来获取,dispatch_walltime须要使用一个timespec的结构体来获得dispatch_time_t。一般dispatch_time用于计算相对时间,dispatch_walltime用于计算绝对时间,我写了一个把NSDate转成dispatch_time_t的Swift方法:
func getDispatchTimeByDate(date: NSDate) -> dispatch_time_t {
let interval = date.timeIntervalSince1970
var second = 0.0
let subsecond = modf(interval, &second)
var time = timespec(tv_sec: __darwin_time_t(second), tv_nsec: (Int)(subsecond * (Double)(NSEC_PER_SEC)))
return dispatch_walltime(&time, 0)
}
这个方法接收一个NSDate对象,而后把NSDate转成dispatch_walltime须要的timespec结构体,最后再把dispatch_time_t返回,一样是在10秒后执行,以前的代码在调用部分须要修改为:
var time = getDispatchTimeByDate(NSDate(timeIntervalSinceNow: 10))
dispatch_after(time, globalQueue) { () -> Void in
println("在10秒后执行")
}
这就是经过绝对时间来使用dispatch_after的例子。
dispatch_group
可能常常会有这样一种状况:咱们如今有3个Block要执行,咱们不在意它们执行的顺序,咱们只但愿在这3个Block执行完以后再执行某个操做。这个时候就须要使用dispatch_group了:
let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
let group = dispatch_group_create()
dispatch_group_async(group, globalQueue) { () -> Void in
println("1")
}
dispatch_group_async(group, globalQueue) { () -> Void in
println("2")
}
dispatch_group_async(group, globalQueue) { () -> Void in
println("3")
}
dispatch_group_notify(group, globalQueue) { () -> Void in
println("completed")
}
输出的顺序与添加进队列的顺序无关,由于队列是Concurrent Dispatch Queue,但“completed”的输出必定是在最后的:
[plain] view plain copy
print?
1 312
2 completed
除了使用dispatch_group_notify函数能够获得最后执行完的通知外,还可使用
let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
let group = dispatch_group_create()
dispatch_group_async(group, globalQueue) { () -> Void in
println("1")
}
dispatch_group_async(group, globalQueue) { () -> Void in
println("2")
}
dispatch_group_async(group, globalQueue) { () -> Void in
println("3")
}
//使用dispatch_group_wait函数
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
println("completed")
须要注意的是,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_barrier_async
dispatch_barrier_async就如同它的名字同样,在队列执行的任务中增长“栅栏”,在增长“栅栏”以前已经开始执行的block将会继续执行,当dispatch_barrier_async开始执行的时候其余的block处于等待状态,dispatch_barrier_async的任务执行完后,其后的block才会执行。咱们简单的写个例子,假设这个例子有读文件和写文件的部分:
func writeFile() {
NSUserDefaults.standardUserDefaults().setInteger(7, forKey: "Integer_Key")
}
func readFile(){
print(NSUserDefaults.standardUserDefaults().integerForKey("Integer_Key"))
}
写文件只是在NSUserDefaults写入一个数字7,读只是将这个数字打印出来而已。咱们要避免在写文件时候正好有线程来读取,就使用dispatch_barrier_async函数:
NSUserDefaults.standardUserDefaults().setInteger(9, forKey: "Integer_Key")
let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(globalQueue) {self.readFile()}
dispatch_async(globalQueue) {self.readFile()}
dispatch_async(globalQueue) {self.readFile()}
dispatch_async(globalQueue) {self.readFile()}
dispatch_barrier_async(globalQueue) {self.writeFile() ; self.readFile()}
dispatch_async(globalQueue) {self.readFile()}
dispatch_async(globalQueue) {self.readFile()}
dispatch_async(globalQueue) {self.readFile()}
咱们先将一个9初始化到NSUserDefaults的Integer_Key中,而后在中间执行dispatch_barrier_async函数,因为这个队列是一个Concurrent Dispatch Queue,能同时并发多少线程是由系统决定的,若是添加dispatch_barrier_async的时候,其余的block(包括上面4个block)尚未开始执行,那么会先执行dispatch_barrier_async里的任务,其余block所有处于等待状态。若是添加dispatch_barrier_async的时候,已经有block在执行了,那么dispatch_barrier_async会等这些block执行完后再执行。
dispatch_apply
dispatch_apply会将一个指定的block执行指定的次数。若是要对某个数组中的全部元素执行一样的block的时候,这个函数就显得颇有用了,用法很简单,指定执行的次数以及Dispatch Queue,在block回调中会带一个索引,而后就能够根据这个索引来判断当前是对哪一个元素进行操做:
let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_apply(10, globalQueue) { (index) -> Void in
print(index)
}
print("completed")
因为是Concurrent Dispatch Queue,不能保证哪一个索引的元素是先执行的,可是“completed”必定是在最后打印,由于dispatch_apply函数是同步的,执行过程当中会使线程在此处等待,因此通常的,咱们应该在一个异步线程里使用dispatch_apply函数:
let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(globalQueue, { () -> Void in
dispatch_apply(10, globalQueue) { (index) -> Void in
print(index)
}
print("completed")
})
print("在dispatch_apply以前")
dispatch_suspend / dispatch_resume
某些状况下,咱们可能会想让Dispatch Queue暂时中止一下,而后在某个时刻恢复处理,这时就可使用dispatch_suspend以及dispatch_resume函数:
//暂停
dispatch_suspend(globalQueue)
//恢复
dispatch_resume(globalQueue)
暂停时,若是已经有block正在执行,那么不会对该block的执行产生影响。dispatch_suspend只会对还未开始执行的block产生影响。
Dispatch Semaphore
信号量在多线程开发中被普遍使用,当一个线程在进入一段关键代码以前,线程必须获取一个信号量,一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待前面的线程释放信号量。
信号量的具体作法是:当信号计数大于0时,每条进来的线程使计数减1,直到变为0,变为0后其余的线程将进不来,处于等待状态;执行完任务的线程释放信号,使计数加1,如此循环下去。
下面这个例子中使用了10条线程,可是同时只执行一条,其余的线程处于等待状态:
let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
let semaphore = dispatch_semaphore_create(1)
for i in 0 ... 9 {
dispatch_async(globalQueue, { () -> Void in
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
let time = dispatch_time(DISPATCH_TIME_NOW, (Int64)(2 * NSEC_PER_SEC))
dispatch_after(time, globalQueue) { () -> Void in
print("2秒后执行")
dispatch_semaphore_signal(semaphore)
}
})
}
取得信号量的线程在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自动管理,若是是在此以前的版本,须要本身手动释放。
dispatch_once
dispatch_once函数一般用在单例模式上,它能够保证在程序运行期间某段代码只执行一次,若是咱们要经过dispatch_once建立一个单例类,在Swift能够这样:
class SingletonObject {
class var sharedInstance : SingletonObject {
struct Static {
static var onceToken : dispatch_once_t = 0
static var instance : SingletonObject? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = SingletonObject()
}
return Static.instance!
}
}
这样就能经过GCD的安全机制保证这段代码只执行一次。