您的位置:首页 > 移动开发 > IOS开发

GCD理解

2015-10-09 23:01 435 查看

GCD

GCD (Grand Central Dispatch) 是 libdispatch 的市场名称,而 libdispatch 作为 Apple 的一个库,为并发代码在多核硬件上执行提供有力支持。

在iOS多线程编程中,GCD占有相当重要的地位。因为相对于 NSThread 和 NSOperation, GCD的使用更简单方便并且实现了真正的硬件多核支持,开发者要做的只是定义想执行的任务并追加到适当的 Dispatch Queue中。

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 创建

通过CGD API

func dispatch_queue_create(_ label: UnsafePointer<Int8>, _ 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_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

保证只执行一次的 dispatch_once

public f
4000
unc 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,就会变成线程安全了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios