
本文共 7494 字,大约阅读时间需要 24 分钟。
本文内容来自
iOS Concurrency-队列、DispatchGroup
Swift 3
在Swift3中,GCD有关的API发生了一些变化,可参考:
获取系统队列
1.获取系统主队列:let mainQ = DispatchQueue.main
2.获取系统的全局队列
DispatchQueue.global(qos: .userInteractive)DispatchQueue.global(qos: .userInitiated)DispatchQueue.global() // .default qosDispatchQueue.global(qos: .utility)DispatchQueue.global(qos: .background)DispatchQueue.global(qos: .unspecified)
QoS
的全称是quality of service
。在Swift 3中,它是一个结构体,用来制定队列或者任务的重要性。
User Interactive
和用户交互相关,比如动画等等优先级最高。比如用户连续拖拽的计算User Initiated
需要立刻的结果,比如push一个ViewController之前的数据计算Utility
可以执行很长时间,再通知用户结果。比如下载一个文件,给用户下载进度。Background
用户不可见,比如在后台存储大量数据
用户创建队列
1.创建串行队列let mySerialQ = DispatchQueue(label: "com.raywenderlich.serial")
2.创建并行队列
let workerQ = DispatchQueue(label: “com.raywenderlich.worker",attributes: .concurrent)
常用的方式是:
DispatchQueue.global().async { // do expensive synchronous task DispatchQueue.main.async { // update UI when task finishes }}
串行队列通常用来set
和get
属性的值,避免race conditions
private let internalQueue = DispatchQueue(label:"com.raywenderlich.person.internal")var name: String { get { return internalQueue.sync { internalName } } set (newName) { internalQueue.sync { internalName = newName } }}
如何理解串行队列的async
和sync
?
async
与sync
的区别,在于return返回的时机,sync
需等待block
执行完毕再返回,而async
则不需等待 DispatchGroup
如果想在dispatch_queue
中所有的任务执行完成后再做某种操作可以使用DispatchGroup
。原先的dispatch_group_t
由现在的DispatchGroup
对象代替。
let slowAddGroup = DispatchGroup()
group
中每个任务都完成后,会调用notify
方法
wait
方法,会等待group
完成,要注意的是,此方法是同步,会阻塞当前的线程 如下的例子,模拟一个耗时操作slowAdd
,对数组中的元素相加:
public func slowAdd(_ input: (Int, Int)) -> Int { sleep(1) return input.0 + input.1}
对多个数据操作,在全部任务完成后得到通知,再做其它的操作,如下:
let workerQueue = DispatchQueue(label: "com.raywenderlich.worker", attributes: .concurrent)let numberArray = [(0,1), (2,3), (4,5), (6,7), (8,9)]print("=== Group of sync tasks ===\n")let slowAddGroup = DispatchGroup()for inValue in numberArray{ workerQueue.async(group: slowAddGroup) { let result = slowAdd(inValue) print("\(inValue) = \(result)") }}//全部任务完成后的调用let defaultQueue = DispatchQueue.global()slowAddGroup.notify(queue: defaultQueue){ print("Done")}//等待group中任务完成print("wait before......")slowAddGroup.wait(timeout: DispatchTime.distantFuture)print("wait after......")
其输出结果如下:
=== Group of sync tasks ===wait before......(0, 1) = 1(2, 3) = 5(4, 5) = 9(8, 9) = 17(6, 7) = 13Donewait after......
还有一个问题是,假设你想把一个异步执行的方法包含在一个dipatch group
中,而这异步执行的方法并没有提供一个dipatch group
参数,所以该如何对现存的asynchronous API
使用dispatch groups
?在实际的项目中,经常会遇到这样的例子,如,有多个网络请求,需要在所有网络请求完成后,做某些事情,该怎么做呢?
group.enter()
和group.leave()
,这两者要成对的出现 通过例子来说明,还是上面slowAdd
的例子,不过,把它封装在一个异步的方法中执行:
1.runQueue
表示执行的队列
completionQueue
表示完成task后,completion回调执行的队列 func asyncAdd(_ input: (Int, Int), runQueue: DispatchQueue, completionQueue: DispatchQueue, completion: @escaping (Int, Error?) -> ()) { runQueue.async { var error: Error? error = .none let result = slowAdd(input) completionQueue.async { completion(result, error) } }}
现在,有一组数据要执行asyncAdd
,想要在所有的任务都执行完后,得到通知。
asyncAdd_Group
方法,把asyncAdd
添加到group中,如下: func asyncAdd_Group(_ input: (Int, Int), runQueue: DispatchQueue, completionQueue: DispatchQueue, group: DispatchGroup, completion: @escaping (Int, Error?) -> ()) { group.enter() asyncAdd(input, runQueue: runQueue, completionQueue: completionQueue) { (result, error) in completion(result, error) group.leave() }}
注意上面的group.enter()
和group.leave()
调用的过程如下:
//创建grouplet wrappedGroup = DispatchGroup()//循环调用asyncAdd_Groupfor pair in numberArray { asyncAdd_Group(pair, runQueue: workerQueue, completionQueue: defaultQueue, group: wrappedGroup, completion: { (result, error) in print("\(pair) = \(result)") })}//通知wrappedGroup.notify(queue: defaultQueue) { print("Async Done!")}
其输出结果如下,达到预期的效果:
(6, 7) = 13(0, 1) = 1(2, 3) = 5(4, 5) = 9(8, 9) = 17Async Done!
实际使用的例子
1.UIView
的动画API
是异步的,所以当有多个动画的时候,可以使用DispatchGroup
,在动画都完成是得到通知 如下,扩展UIView
,原理与上面讲的一样,使用DispatchGroup
,添加group.enter()
和group.leave()
extension UIView { static func animate(withDuration duration: TimeInterval, animations: @escaping () -> Void, group: DispatchGroup, completion: ((Bool) -> Void)?) { group.enter() UIView.animate(withDuration: duration, animations: animations) { (success) in completion?(success) group.leave() } }}
使用方式如下:
首先创建group
let animationGroup = DispatchGroup()
然后,调用上面的扩展方法即可:
UIView.animate(withDuration: 1, animations: { box.center = CGPoint(x: 150, y: 150)}, group: animationGroup) { _ in UIView.animate(withDuration: 2, animations: { box.transform = CGAffineTransform(rotationAngle: .pi/4) }, group: animationGroup, completion: .none)}animationGroup.notify(queue: DispatchQueue.main) { print("Animations Completed!")}
并发问题
在并发编程中可能出现的问题:
- Race condition
- Priority inversion
- Deadlock
一些建议:
- One QoS for tasks accessing shared resource
- Serial queue to access shared resource
- Avoid Operation dependency cycles
- Be careful when calling sync()
- Never call sync() on the current queue
- Never ever call sync() from the main queue
实际问题
1.在异步调用的时候要考虑线程安全的问题
如下的Person
类,其中有个changeName
方法来修改firstName
和lastName
,注意方法中延迟: open class Person { private var firstName: String private var lastName: String public init(firstName: String, lastName: String) { self.firstName = firstName self.lastName = lastName } //修改name open func changeName(firstName: String, lastName: String) { randomDelay(maxDuration: 0.2) self.firstName = firstName randomDelay(maxDuration: 1) self.lastName = lastName } open var name: String { return "\(firstName) \(lastName)" }}//延迟func randomDelay(maxDuration: Double) { let randomWait = arc4random_uniform(UInt32(maxDuration * Double(USEC_PER_SEC))) usleep(randomWait)}
如下,在一个并发的队列中异步执行changeName
操作:
let workerQueue = DispatchQueue(label: "com.raywenderlich.worker", attributes: .concurrent)let nameChangeGroup = DispatchGroup()let nameList = [("Charlie", "Cheesecake"), ("Delia", "Dingle"), ("Eva", "Evershed"), ("Freddie", "Frost"), ("Gina", "Gregory")]for (idx, name) in nameList.enumerated() { workerQueue.async(group: nameChangeGroup) { usleep(UInt32(10_000 * idx)) nameChangingPerson.changeName(firstName: name.0, lastName: name.1) print("Current Name: \(nameChangingPerson.name)") }}nameChangeGroup.notify(queue: DispatchQueue.global()) { print("Final name: \(nameChangingPerson.name)") PlaygroundPage.current.finishExecution()}
其输出结果为:
Current Name: Gina DingleCurrent Name: Gina FrostCurrent Name: Gina CheesecakeCurrent Name: Gina GregoryCurrent Name: Gina EvershedFinal name: Gina Evershed
会发现结果完全不正确,这里的问题是一个线程在写的时候,另一个线程也在写,所以有race condition
的问题,所以Person
类并不是线程安全的
此时可以使用Dispatch Barrier创建线程安全的Person
class ThreadSafePerson: Person { let isolationQueue = DispatchQueue(label: "com.raywenderlich.person.isolation", attributes: .concurrent) override func changeName(firstName: String, lastName: String) { isolationQueue.async(flags: .barrier) { super.changeName(firstName: firstName, lastName: lastName) } } override var name: String { return isolationQueue.sync { return super.name } }}
2.SYNC READING/WRITING VALUES
private let internalQueue = DispatchQueue(label:"com.raywenderlich.person.internal")var name: String { get { return internalQueue.sync { internalName } } set (newName) { internalQueue.sync { internalName = newName } }}
3.通过Thread Sanitizer
查找出可能的线程问题
转载地址:https://windzen.blog.csdn.net/article/details/52774454 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
关于作者
