GCD和Operation/OperationQueue 看这一篇文章就够了

 

Grand Central Dispatch简称GCD,是苹果公司为多核的并行运算提出的解决方案, 允许程序将任务切分为多个单一任务然后提交至工作队列来并发地或者串行地执行。...



Grand Central Dispatch简称GCD,是苹果公司为多核的并行运算提出的解决方案, 允许程序将任务切分为多个单一任务然后提交至工作队列来并发地或者串行地执行。GCD会自动利用更多的CPU内核(比如双核、四核), 自动管理线程的生命周期(创建线程、调度任务、销毁线程)。

下面逐一介绍DispatchQueue, Operation和OperationQueue.

文中的示例代码均可参见我的GitHub: https://github.com/zhihuitang/GCDExample

1. DispatchQueue

GCD的基本概念就是DispatchQueue。DispatchQueue是一个对象,它可以接受任务,并将任务以FIFO(先到先执行)的顺序来执行。DispatchQueue可以是并发的或串行的, 它有3种队列类型:

  • Main queue
  • Global queue
  • Custom queue
1.1 Main queue(串行Serial)

Main queue运行在系统主线程.它是一个串行队列,我们所有的UI刷新都发生在Main queue. 获取Main queue的方法很简单:

// Get the main queue
let mainQueue = DispatchQueue.main


如果要在非Main queue线程中直接刷新UI, 运行时会出exception. 一般的做法是将代码放在Main queue中异步执行:

function DisplayWindowSize(){

var w=window.innerWidth

|| document.documentElement.clientWidth

|| document.body.clientWidth;
}


1.2 Global queues(并行Concurrent)

如果要在后台执行非UI相关的工作, 一般把这部分工作放在Global queue. Global queue是一种系统内共享的并行的队列. 申请Global queue的方法很简单:

// Get the .userInitiated global dispatch queue
let userQueue = DispatchQueue.global(qos: .userInitiated)
// Get the .default global dispatch queue
let defaultQueue = DispatchQueue.global()


Global queue队列有四种优先级: 高, 缺省, 低, 后台. 在实际申请Global queue时,我们不需直接指定优先级, 只需申明所需的(QoS)类型, Qos间接的决定了这些queue的优先级

例如:

DispatchQueue.global(qos: .userInteractive)
DispatchQueue.global(qos: .userInitiated)
DispatchQueue.global()  // .default qos
DispatchQueue.global(qos: .utility)
DispatchQueue.global(qos: .background)
DispatchQueue.global(qos: .unspecified)
The QoS classes are:

  • User-interactive
表示为了给用户提供一个比较好的体验, 任务必须立即完成. 主要用于UI刷新, 低延迟的事件处理等. 在整个App内, 这种类型的任务不宜太多. 它是最高优先级的.

  • User-initiated
用于UI发起异步任务,用户在等待执行的结果, 这种queue是高优先级的.

DispatchQueue.global(qos: .userInitiated).async {

let overlayImage = self.faceOverlayImageFromImage(self.image)

DispatchQueue.main.async {

self.fadeInNewImage(overlayImage)

}
}


  • Utility
长时间运行的任务, 典型情况是App中会有一个进度条表示任务的进度. 主要用于 计算, I/O, 网络交互等, 主要为节能考虑. 这种queue是低优先级的.

  • Background
任务在运行, 但用户感觉不到它在运行的场景. 主要用于不需要用户干涉,对时间不敏感的获取数据等任务, 这种queue是后台优先级,属于最低优先级的那一种.

1.3 Custom queues

用户创建的Custom queues默认的是串行的, 如果指定了attributes为concurrent则为并行的. 下面我们用代码演示串行队列/并行队列的区别.

  • Serial Queue
除了DispatchQueue.main是并行的queue外, 你也可以创建自己的并行queue(缺省为串行)

func task1() {

print("Task 1 started")

// make task1 take longer than task2

sleep(1)

print("Task 1 finished")
}
func task2() {

print("Task 2 started")

print("Task 2 finished")
}
example(of: "Serial Queue") {

let mySerialQueue = DispatchQueue(label: "com.crafttang.serialqueue")

mySerialQueue.async {

task1()

}

mySerialQueue.async {

task2()

}
}
sleep(2)
上面代码的输出为:

--- Example of: Serial Queue ---
Task 1 started
[ spent: 0.00026 ] seconds
Task 1 finished
Task 2 started
Task 2 finished


从上可以看出, task1和task2是顺序运行的, 只有task1执行完了后task2才可以执行.由于task1和task2都是异步(asyc)执行的, 所以不会阻塞当前线程, 2个任务执行的时间只有0.00026秒.

  • Concurrent Queue
创建用户自己的并行queue, 声明.concurrent属性即可:

example(of: "Concurrent Queue") {

let concurrentQueue = DispatchQueue(label: "com.crafttang.currentqueue", attributes: .concurrent)

concurrentQueue.async {

task1()

}

concurrentQueue.async {

task2()

}
}
sleep(2)


上面的输出为:

--- Example of: Concurrent Queue ---
Task 1 started
Task 2 started
Task 2 finished
[ spent: 0.00034 ] seconds
Task 1 finished
从上面可以看出, task1和task2是并发执行的, task1启动后, 由于执行时间需要1s, 这个时候task2也可以同步运行, 所以我们可以看到task1启动后, 立即启动task2, 然后task2完成, task1完成.

task1和task2都是异步(async)运行的,所以它们花费的时间仍然很短, 只有0.00034秒.

同步(Synchronous) vs. 异步(Asynchronous)

  • 对于一个任务(function), 可以在GCD队列里同步运行, 也可以异步运行。
  • 同步运行的任务, 不开启新的线程, 会阻塞当前线程, 等任务完成才返回。
  • 异步运行的任务, 会开启新的线程, 不会阻塞当前线程, 分发任务后立即返回,不用等任务完成。

2. DispatchGroup

DispatchGroup实例用来追踪不同队列中的不同任务。当group里所有事件都完成GCD API有两种方式发送通知,第一种是DispatchGroup.wait,会阻塞当前进程,等所有任务都完成或等待超时。第二种方法是使用DispatchGroup.notify,异步执行闭包,不会阻塞当前线程。

example(of: "DispatchGroup") {

let workerQueue = DispatchQueue(label: "com.crafttang.dispatchgroup", attributes: .concurrent)

let dispatchGroup = DispatchGroup()

let numberArray: [(Any,Any)] = [("A", "B"), (2,3), ("C", "D"), (6,7), (8,9)]

for inValue in numberArray {

workerQueue.async(group: dispatchGroup) {

let result = slowJoint(inValue)

print("Result = (result)")

}

}

//dispatchGroup.wait(timeout: .now() + 1)

let notifyQueue = DispatchQueue.global()

dispatchGroup.notify(queue: notifyQueue) {

print("


    关注 Cocoa开发者社区


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册