GCD (Grand Central Dispatch) 是 libdispatch 的市场名称,而 libdispatch 作为 Apple 的一个库,为并发代码在多核硬件上执行提供有力支持。
在iOS多线程编程中,GCD占有相当重要的地位。因为相对于 NSThread 和 NSOperation, GCD的使用更简单方便并且实现了真正的硬件多核支持,开发者要做的只是定义想执行的任务并追加到适当的 Dispatch Queue中。
dispatch_queue 是一个执行处理的等待队列,是一个FIFo(先进先出)队列,这样就保证了先放进队列的任务能够
先执行。
dispatch_queue 是线程安全的,在并发任务中被调用时不会产生任务问题。所以可以放心使用。
dispatch_queu类型:
1) 串行 dispatch_queue 在串行队列一次只执行一个任务,并且按照我们添加到队列的顺序来执行。
在串行队列中,在同一个时间间隔内只有一个任务可以执行,当上一个任务结束后才会开始下一个任务。
2) 并发 dispatch_queue 在并发队列中一次可以执行多个,它们会按照被添加的顺序开始执行。
在并发队列中同一个时间间隔内可以有多个任务同时执行。
在并发队列中的任务可以不用等待现在执行中的处理结束,这样就可以并行执行多个处理,但并行执行的处理数量
取决于当前系统状态。即iOS和OS X 基于dispatch Queue中的处理数、cpu核心以及CPU负荷等当前系统的状态
来绝定Concurrent Dispatch Queue中执行的处理数。
可以简单的理解为并发是一种能力,可以允许多个任务执行。并行是真正的在同一时间内执行多个任务。所以要想可以并行
首先必须可以并发。但并发不要求并行。并行在多核中执行起来相对容易,可以给每个核分配一个线程执行各自的任务。
但在单核中执行并行就不那么容易了,在单核中必须使用时间片轮转法。例如,有两个线程Thread1,Thread2,在一个时间
片内先执行Thread1的任务,然后保留现场,在一下个时间片执行Thread2,当时间片结束时保留现场并切换到Thrad1开始
下一个时间片.由于这个时间片相当相当短,给我们的感觉就是在同时执行。
dispatch_queu 创建
func dispatch_queue_create(_ label: UnsafePointer, _ attr: dispatch_queue_attr_t!) -> dispatch_queue_t!
每次调用 dispatch_queue_create 都会创建一个新的dispatch_queue_t和一个新的线程,所以过多的创建
dispatch_queue 会创建过多的线程,就会消耗大量内存,引起大量的上下文切换。这样多个线程的queue就行成了一个
并行的任务队列,这时候如果同时去写一个数据就可能出问题了。
2.获取系统中已存在的dispatch_queue
Main Dispatch Queue:在主线程中执行的串行dispatch_queue,因为主线程只有一个,所以Main Dispatch Queue也只存在一个。Main Dispatch 中的所有任务都是在主线程的runloop中执行的,跟NSObject的
performSelectOnMainThread一样。所以执行UI更新等需要在主线程中执行的任务必须添加到Main Dispatch
queue中.
Global Dispatch Queue:全局可用的并行队列。队列有4个执行的优先等级。High Priority, Default
Low, BackGround.
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
// 刷新UI
});
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(globalQueue, ^{
// 费时且不需要阻塞线程的操作
});
dispatch_async(dispatch_get_global_queue(0, 0), { () -> Void in
let url:NSURL = NSURL(string:"http://7xlgnt.com1.z0.glb.clouddn.com/Serial.png")!
let imageData: NSData? = NSData(contentsOfURL:url)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if let imageData = imageData {
imageView.image = UIImage(data: imageData)
}
})
})
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0)) { () -> Void in
print("come here")
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_MSEC)), dispatch_get_global_queue(0, 0)) { () -> Void in
print("come here2")
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_USEC)), dispatch_get_global_queue(0, 0)) { () -> Void in
print("come here3")
}
我们先看几个值
print(NSEC_PER_SEC) 结果: 1000000000 皮秒 1000000000 = 1秒
print(NSEC_PER_MSEC) 结果: 1000000 微妙 1000000 = 1 秒
print(NSEC_PER_USEC) 结果: 1000 毫秒 1000 毫秒 = 1秒
print(DISPATCH_TIME_NOW) 0
print(DISPATCH_TIME_FOREVER) 18446744073709551615
print(UInt64.max) 18446744073709551615
public func dispatch_time(when: dispatch_time_t, _ delta: Int64) -> dispatch_time_t
when 什么时候开始计算延迟,是一个Uint64的值, 这里的值都是皮秒
_delta 延迟多少皮秒执行。
要延迟1秒我们可以这样:
dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_SEC)
dispatch_time(DISPATCH_TIME_NOW,Int64(1000 * NSEC_PER_MSEC)
dispatch_time(DISPATCH_TIME_NOW,Int64(1000000 * NSEC_PER_USEC)
所以上面的代码执行结果是:
come here3
come here2
come here
public func dispatch_once(predicate: UnsafeMutablePointer, _ block: dispatch_block_t)
单例是我们在代码中最常见的只执行一次的例子。有如下一个类
class Car {
var name:String?
static var car:Car?
class func shareInstance() -> Car {
if (car == nil) {
car = Car()
print("开始初始化")
}
return car!
}
}
上面的类中有一个shareInstance的方法来生成一个单例对象,但是上面的方法并不是线程安全的,如果在多线程中执行上述方法就会产问题
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
var car = Car.shareInstance()
};
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
var car = Car.shareInstance()
};
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
var car = Car.shareInstance()
};
执行时我们可以看到输出了三次 “开始初始化”,这说明car == nil并不能有效的去判断当前是不是已经开始初始化了,因为当第
一个的初始化还未完成时第二个的初始已经开始了,然后第三个也开始初始化,所以说看到输出了三个“开始初始化”
class Car {
var name:String?
class var shareInstance:Car {
struct Static {
static var car:Car?
static var onceToken : dispatch_once_t = 0
}
dispatch_once(&Static.onceToken) { () -> Void in
Static.car = Car()
}
return Static.car!
}
}
如果换成上面的代码使用dispatch_once,就会变成线程安全了。