当前位置:   article > 正文

swift--多线程_swift多线程

swift多线程

本文参考:《iOS移动开发从入门到精通 第二版》(编著:李发展 ) 第14章 多线程技术,感兴趣的可以读下原著

本文主要描述iOS线程的基本概念,以及Thread、Operation和grand central dispatch三种多线程技术的使用。

一、 任务、进程和线程简述

1.1 任务 Task

任务既可以是一个线程,也可以是一个进程,是指为了达到目的的一组操作集合。

1.2 进程 Process

进程是一个独立的应用程序,在内存中具有独立的数据和代码空间,其拥有的数据和变量只属于他自己。是系统资源分配和调度的独立单位

1.3 线程 Thread

线程是比进程更小的能独立运行的基本单位,也是cpu调度和分派的基本单位。线程存在于进程之中,一个进程可以由多个线程构成。

二、 线程的stack space

iOS主线程栈空间默认1MB,子线程默认512KB,macOS为8MB。它不是立刻被创建分配,而是在使用过程中逐渐增加。子线程允许分配的最小栈空间为16KB,并且为4的倍数。

三、 线程优先级

线程的优先级属性是一个0.0~1.0的浮点值,1最高,0最小,默认0.5。注意,最高优先级不是100%会被优先执行,只是得到cpu调度的概率更高。

四、 线程的生命周期

创建,就绪,运行(在线程执行结束之前,会在就绪和运行之间来回切换),阻塞,消亡

五、 线程和RunLoop

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

  1. //用Thread方式下载一张图片
  2. let imageUrl = "http://www.antu58.club/images/work-1.jpg"
  3. //初始化一个线程实例
  4. let thread = Thread.init(target: self, selector: #selector(threadTask(path:)), object: imageUrl)
  5. //启动该线程
  6. thread.start()
  1. @objc func threadTask(path:String) {
  2. let url = URL(string: path)
  3. var data:Data!
  4. do {
  5. try data = Data(contentsOf: url!)
  6. let image = UIImage(data: data)
  7. //在主线程显示图片
  8. self.perform(#selector(showImage(image:)), on: Thread.main, with: image, waitUntilDone: true)
  9. } catch {
  10. print("error")
  11. }
  12. }
  1. @objc func showImage(image:UIImage) {
  2. self.image.image = image
  3. }

 给thread加锁使线程同步

iOS下常用的锁为Lock对象,加锁用lock方法,解锁用unlock方法。当一个Lock对象,调用过一次lock方法之后,并且没有调用unlock方法前,任何线程都不能再对此Lock对象加锁。

iOS还提供recursiveLock、ConditionLock等类型的锁。recursive可以重复lock,但解锁时需要调用相同次数的unlock。ConditionLock是一个带有条件的锁,可以根据条件对线程进行加锁。

实例:

  1. import UIKit
  2. class ViewController: UIViewController {
  3. var booksCount = 100
  4. var soldBooksCount = 0
  5. var lock:NSLock!
  6. override func viewDidLoad() {
  7. super.viewDidLoad()
  8. // Do any additional setup after loading the view.
  9. lock = NSLock()
  10. let salesmanA = Thread.init(target: self, selector: #selector(sellBook), object: nil)
  11. salesmanA.name = "销售A"
  12. salesmanA.start()
  13. let salesmanB = Thread.init(target: self, selector: #selector(sellBook), object: nil)
  14. salesmanB.name = "销售B"
  15. salesmanB.start()
  16. let salesmanC = Thread.init(target: self, selector: #selector(sellBook), object: nil)
  17. salesmanC.name = "销售C"
  18. salesmanC.start()
  19. }
  20. @objc func sellBook(){
  21. while true {
  22. lock.lock()
  23. if booksCount > 0 {
  24. Thread.sleep(forTimeInterval: 0.01)
  25. soldBooksCount += 1
  26. booksCount -= 1
  27. let threadName = Thread.current.name
  28. print("当前销售员为:\(String(describing: threadName)), 已售出:\(soldBooksCount), 剩余:\(booksCount)")
  29. } else {
  30. //结束
  31. Thread.exit()
  32. }
  33. lock.unlock()
  34. }
  35. }
  36. }

假如不执行加锁操作,就会出现这种情况

七、Operation技术使用

过多的线程会消耗大量系统资源,导致程序变慢卡顿。和Thread相比,operation的好处是不需要担心线程管理和数据同步。

Operation是一个抽象类,使用它的子类BlockOperation创建一个operation子类的对象,并把对象添加到QperationQueue队列执行。

通过设置OperationQueu的maxConcurrentOperationCount属性,我们可以控制同时运行的线程数。

实例:

  1. import UIKit
  2. class ViewController: UIViewController {
  3. @IBOutlet weak var imageViewA: UIImageView!
  4. @IBOutlet weak var imageViewB: UIImageView!
  5. override func viewDidLoad() {
  6. super.viewDidLoad()
  7. // Do any additional setup after loading the view.
  8. let imageUrl = "http://www.antu58.club/images/work-1.jpg"
  9. let taskA = getOperation(name: "taskA", imageUrl: imageUrl, isTopOne: true)
  10. let taskB = getOperation(name: "taskB", imageUrl: imageUrl, isTopOne: false)
  11. let queue = OperationQueue()
  12. queue.maxConcurrentOperationCount = 1
  13. queue.addOperation(taskA)
  14. queue.addOperation(taskB)
  15. for operation in queue.operations {
  16. print("Operation: \(String(describing: operation.name))")
  17. }
  18. }
  19. func getOperation(name:String, imageUrl:String, isTopOne:Bool) -> BlockOperation {
  20. let dowonLoad = BlockOperation {
  21. let url = URL(string: imageUrl)
  22. var data:Data!
  23. do {
  24. try data = Data(contentsOf: url!)
  25. let image = UIImage(data: data)
  26. //在主线程显示图片
  27. if isTopOne {
  28. self.perform(#selector(self.showImage1(image:)), on: Thread.main, with: image, waitUntilDone: true)
  29. } else {
  30. self.perform(#selector(self.showImage2(image:)), on: Thread.main, with: image, waitUntilDone: true)
  31. }
  32. } catch {
  33. print("error")
  34. }
  35. }
  36. dowonLoad.name = name
  37. return dowonLoad
  38. }
  39. @objc func showImage1(image:UIImage) {
  40. self.imageViewA.image = image
  41. }
  42. @objc func showImage2(image:UIImage) {
  43. self.imageViewB.image = image
  44. }
  45. }

八、Grand Central Dispatch的使用

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地址信息:

  1. let apiUrl = URL(string: "http://taobao.com/serveice/getIpInfo.php?=27.156.152.57")
  2. let globalQueue = DispatchQueue.global()
  3. //开启一个新的线程
  4. globalQueue.async {
  5. let result = try? Data(contentsOf: apiUrl!)
  6. let message = String(data: result!, encoding: .utf8)
  7. //在主线程同步UI信息
  8. DispatchQueue.main.async {
  9. self.label.text = message
  10. }
  11. }

实例2 DispatchGroup 调度组的使用:

有时我们需要在几件事结束之后,再去执行一个任务,这样的事就可以借助dispatchGroup,他可以将多个block拼成一组,以监测这些block全部完成或者等待全部完成时发出消息。

  1. print("开始任务")
  2. let group = DispatchGroup()
  3. let globalQueue = DispatchQueue.global()
  4. let task1 = DispatchWorkItem{
  5. print("task1")
  6. }
  7. let task2 = DispatchWorkItem{
  8. print("task2")
  9. }
  10. let task3 = DispatchWorkItem{
  11. print("task3")
  12. }
  13. globalQueue.async(group: group, execute: task1)
  14. globalQueue.async(group: group, execute: task2)
  15. globalQueue.async(group: group, execute: task3)
  16. group.notify(queue: globalQueue) {
  17. print("所有任务已经完成")
  18. }

最后用GCD的方式改写一下之前售书案例

  1. import UIKit
  2. class ViewController: UIViewController {
  3. @IBOutlet weak var label: UILabel!
  4. var booksCount = 100
  5. var soldBooksCount = 0
  6. var lock:NSLock = NSLock()
  7. override func viewDidLoad() {
  8. super.viewDidLoad()
  9. let group = DispatchGroup()
  10. let globalQueue = DispatchQueue.global()
  11. let task1 = DispatchWorkItem{
  12. self.sellBook()
  13. }
  14. let task2 = DispatchWorkItem{
  15. self.sellBook()
  16. }
  17. let task3 = DispatchWorkItem{
  18. self.sellBook()
  19. }
  20. globalQueue.async(group: group, execute: task1)
  21. globalQueue.async(group: group, execute: task2)
  22. globalQueue.async(group: group, execute: task3)
  23. group.notify(queue: globalQueue) {
  24. print("所有任务已经完成")
  25. }
  26. }
  27. func sellBook(){
  28. while true {
  29. lock.lock()
  30. if booksCount > 0 {
  31. soldBooksCount += 1
  32. booksCount -= 1
  33. print("已售出:\(soldBooksCount), 剩余:\(booksCount)")
  34. DispatchQueue.main.async {
  35. //更新UI
  36. self.label.text = "已售出:\(soldBooksCount), 剩余:\(booksCount)"
  37. }
  38. } else {
  39. return
  40. }
  41. lock.unlock()
  42. }
  43. }
  44. }

补充:GCD延时

  1. //延时两秒
  2. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
  3. //do...
  4. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/691591
推荐阅读
相关标签
  

闽ICP备14008679号