赞
踩
本文参考:《iOS移动开发从入门到精通 第二版》(编著:李发展 ) 第14章 多线程技术,感兴趣的可以读下原著
本文主要描述iOS线程的基本概念,以及Thread、Operation和grand central dispatch三种多线程技术的使用。
1.1 任务 Task
任务既可以是一个线程,也可以是一个进程,是指为了达到目的的一组操作集合。
1.2 进程 Process
进程是一个独立的应用程序,在内存中具有独立的数据和代码空间,其拥有的数据和变量只属于他自己。是系统资源分配和调度的独立单位
1.3 线程 Thread
线程是比进程更小的能独立运行的基本单位,也是cpu调度和分派的基本单位。线程存在于进程之中,一个进程可以由多个线程构成。
iOS主线程栈空间默认1MB,子线程默认512KB,macOS为8MB。它不是立刻被创建分配,而是在使用过程中逐渐增加。子线程允许分配的最小栈空间为16KB,并且为4的倍数。
线程的优先级属性是一个0.0~1.0的浮点值,1最高,0最小,默认0.5。注意,最高优先级不是100%会被优先执行,只是得到cpu调度的概率更高。
创建,就绪,运行(在线程执行结束之前,会在就绪和运行之间来回切换),阻塞,消亡
runloop是一个对收到事件进行处理的循环。每条线程都有唯一的runloop对象,主线程自动创建并运行,子线程需要手动创建并调用run方法。
runloop可以从两个不同的事件源接收消息
1.Input sources投递异步消息,如对象的perform函数
self.perform(#selector(threadTask), with: nil)
2.Timer Sources 在计划时间或者重复的时间间隔内传递消息
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(threadTask), userInfo: "infomation", repeats: true)
- //用Thread方式下载一张图片
- let imageUrl = "http://www.antu58.club/images/work-1.jpg"
- //初始化一个线程实例
- let thread = Thread.init(target: self, selector: #selector(threadTask(path:)), object: imageUrl)
- //启动该线程
- thread.start()
- @objc func threadTask(path:String) {
- let url = URL(string: path)
- var data:Data!
- do {
- try data = Data(contentsOf: url!)
- let image = UIImage(data: data)
- //在主线程显示图片
- self.perform(#selector(showImage(image:)), on: Thread.main, with: image, waitUntilDone: true)
- } catch {
- print("error")
- }
- }
- @objc func showImage(image:UIImage) {
- self.image.image = image
- }
给thread加锁使线程同步
iOS下常用的锁为Lock对象,加锁用lock方法,解锁用unlock方法。当一个Lock对象,调用过一次lock方法之后,并且没有调用unlock方法前,任何线程都不能再对此Lock对象加锁。
iOS还提供recursiveLock、ConditionLock等类型的锁。recursive可以重复lock,但解锁时需要调用相同次数的unlock。ConditionLock是一个带有条件的锁,可以根据条件对线程进行加锁。
实例:
- import UIKit
-
- class ViewController: UIViewController {
-
- var booksCount = 100
- var soldBooksCount = 0
- var lock:NSLock!
-
- override func viewDidLoad() {
- super.viewDidLoad()
- // Do any additional setup after loading the view.
-
- lock = NSLock()
-
- let salesmanA = Thread.init(target: self, selector: #selector(sellBook), object: nil)
- salesmanA.name = "销售A"
- salesmanA.start()
-
- let salesmanB = Thread.init(target: self, selector: #selector(sellBook), object: nil)
- salesmanB.name = "销售B"
- salesmanB.start()
-
- let salesmanC = Thread.init(target: self, selector: #selector(sellBook), object: nil)
- salesmanC.name = "销售C"
- salesmanC.start()
- }
-
- @objc func sellBook(){
- while true {
- lock.lock()
- if booksCount > 0 {
- Thread.sleep(forTimeInterval: 0.01)
- soldBooksCount += 1
- booksCount -= 1
- let threadName = Thread.current.name
-
- print("当前销售员为:\(String(describing: threadName)), 已售出:\(soldBooksCount), 剩余:\(booksCount)")
- } else {
- //结束
- Thread.exit()
- }
- lock.unlock()
- }
- }
-
- }
假如不执行加锁操作,就会出现这种情况
过多的线程会消耗大量系统资源,导致程序变慢卡顿。和Thread相比,operation的好处是不需要担心线程管理和数据同步。
Operation是一个抽象类,使用它的子类BlockOperation创建一个operation子类的对象,并把对象添加到QperationQueue队列执行。
通过设置OperationQueu的maxConcurrentOperationCount属性,我们可以控制同时运行的线程数。
实例:
- import UIKit
-
- class ViewController: UIViewController {
- @IBOutlet weak var imageViewA: UIImageView!
- @IBOutlet weak var imageViewB: UIImageView!
-
- override func viewDidLoad() {
- super.viewDidLoad()
- // Do any additional setup after loading the view.
- let imageUrl = "http://www.antu58.club/images/work-1.jpg"
-
- let taskA = getOperation(name: "taskA", imageUrl: imageUrl, isTopOne: true)
-
- let taskB = getOperation(name: "taskB", imageUrl: imageUrl, isTopOne: false)
-
- let queue = OperationQueue()
- queue.maxConcurrentOperationCount = 1
- queue.addOperation(taskA)
- queue.addOperation(taskB)
-
- for operation in queue.operations {
- print("Operation: \(String(describing: operation.name))")
- }
-
- }
-
-
- func getOperation(name:String, imageUrl:String, isTopOne:Bool) -> BlockOperation {
- let dowonLoad = BlockOperation {
- let url = URL(string: imageUrl)
- var data:Data!
- do {
- try data = Data(contentsOf: url!)
- let image = UIImage(data: data)
- //在主线程显示图片
- if isTopOne {
- self.perform(#selector(self.showImage1(image:)), on: Thread.main, with: image, waitUntilDone: true)
- } else {
- self.perform(#selector(self.showImage2(image:)), on: Thread.main, with: image, waitUntilDone: true)
- }
- } catch {
- print("error")
- }
- }
- dowonLoad.name = name
- return dowonLoad
- }
-
- @objc func showImage1(image:UIImage) {
- self.imageViewA.image = image
- }
-
- @objc func showImage2(image:UIImage) {
- self.imageViewB.image = image
- }
-
- }
GCD是iOS4.0时推出的一个多核编程的解决方案,可以替换thread和Operation.它会自动利用更多的cpu内核,会自动管理生命周期。也推荐大家多使用这个。
GCD 的 Dispatch Queue,它是一个对象,可以接收任务,并且以先到先执行的顺序来执行。dispatch的调度队列可以是并发的,也可以是串行的。
GCD的调度队列由三种类型
1. The main queue
串行队列,和应用主线程功能相同,会在程序主线程中执行,一般是界面元素的更新。可以通过DispatchQueue.main来获得main队列。
2. Global queue 全局队列是并发队列,有高、中、低、后台四个优先级,默认为中。可以通过DispatchQueue.global来获得全局队列。
3. 用户线程队列,通过DispatchQueue.init来获得用户线程队列,可以创建串行或者并行的队列。
实例1 使用GCD查询IP地址信息:
- let apiUrl = URL(string: "http://taobao.com/serveice/getIpInfo.php?=27.156.152.57")
- let globalQueue = DispatchQueue.global()
- //开启一个新的线程
- globalQueue.async {
- let result = try? Data(contentsOf: apiUrl!)
- let message = String(data: result!, encoding: .utf8)
- //在主线程同步UI信息
- DispatchQueue.main.async {
- self.label.text = message
- }
- }
实例2 DispatchGroup 调度组的使用:
有时我们需要在几件事结束之后,再去执行一个任务,这样的事就可以借助dispatchGroup,他可以将多个block拼成一组,以监测这些block全部完成或者等待全部完成时发出消息。
- print("开始任务")
-
- let group = DispatchGroup()
- let globalQueue = DispatchQueue.global()
- let task1 = DispatchWorkItem{
- print("task1")
- }
- let task2 = DispatchWorkItem{
- print("task2")
- }
- let task3 = DispatchWorkItem{
- print("task3")
- }
- globalQueue.async(group: group, execute: task1)
- globalQueue.async(group: group, execute: task2)
- globalQueue.async(group: group, execute: task3)
-
- group.notify(queue: globalQueue) {
- print("所有任务已经完成")
- }
最后用GCD的方式改写一下之前售书案例
-
- import UIKit
-
- class ViewController: UIViewController {
- @IBOutlet weak var label: UILabel!
-
- var booksCount = 100
- var soldBooksCount = 0
- var lock:NSLock = NSLock()
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- let group = DispatchGroup()
- let globalQueue = DispatchQueue.global()
- let task1 = DispatchWorkItem{
- self.sellBook()
- }
- let task2 = DispatchWorkItem{
- self.sellBook()
- }
- let task3 = DispatchWorkItem{
- self.sellBook()
- }
- globalQueue.async(group: group, execute: task1)
- globalQueue.async(group: group, execute: task2)
- globalQueue.async(group: group, execute: task3)
-
- group.notify(queue: globalQueue) {
- print("所有任务已经完成")
- }
-
- }
-
- func sellBook(){
- while true {
- lock.lock()
- if booksCount > 0 {
- soldBooksCount += 1
- booksCount -= 1
-
- print("已售出:\(soldBooksCount), 剩余:\(booksCount)")
- DispatchQueue.main.async {
- //更新UI
- self.label.text = "已售出:\(soldBooksCount), 剩余:\(booksCount)"
- }
- } else {
- return
- }
- lock.unlock()
- }
- }
-
- }
-
补充:GCD延时
- //延时两秒
- DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
- //do...
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。