Swift的多线程技术其实和Objective-C没有区别。Thread是三种正常程序员会使用的多线程中最轻量级的,每一个Thread对象代表着一个线程,但是需要自己管理线程的生命周期和线程的同步。线程同步对数据的加锁会有一定的开销。
哪三种正常程序员会使用的多线程方案,问这个的同学,你,出去~ 前面写了辣么多,Operation、GCD、Thread,都忘啦?
如果要是较真,还有一个多线程技术,叫做pthread。我们会在最后最后稍微说一下它。但是它绝对不是正常程序员现在还需使用的多线程方案。说完这句话不知道会不会被喷死。
1. Thread的三种建立方式
以下所有的代码都是使用陈旧的Swift 3.0编写。
对,你没看错。陈旧的Swift 3.0。 Swift 5.0 都开始了,听到这个消息的时候,内心是五味杂陈的,有点点为Swift担心了。尼玛这4.0 发布没多久,5.0就开始了。 所以坊间才有笑话说,学习iOS开发需要熟练使用Swift1.0 , Swift2.0 ,Swif 2.2...四种语言之类的。
1.1 使用类方法创建,自动运行
一种是带Selector
,一种不带。
Thread.detachNewThread {
print("A new thread,name:\(Thread.current)")
}
//带一个Selector
Thread.detachNewThreadSelector(#selector(threadPrint), toTarget: self, with: nil)
复制代码
通过这种方法创建的thread会自动运行。
1.2 实用构造方法创始化,需手动运行
这里可是使用两种方法直接创建Thread,并运行。
Thread(block: <#T##() -> Void#>)
Thread(target: <#T##Any#>, selector: <#T##Selector#>, object: <#T##Any?#>)
复制代码
下面是个例子,这种方法创建的thread需要手动start
运行
let customThread = Thread(target: self, selector: #selector(threadPrint), object: nil)
customThread.start()
复制代码
1.3 使用NSObject的拓展方法
任何继承自NSObject的都可以很容易的通过这种方式调用点成方法。
来,我们一起来看一下源文件:
extension NSObject {
open func performSelector(onMainThread aSelector: Selector, with arg: Any?, waitUntilDone wait: Bool, modes array: [String]?)
open func performSelector(onMainThread aSelector: Selector, with arg: Any?, waitUntilDone wait: Bool)
@available(iOS 2.0, *)
open func perform(_ aSelector: Selector, on thr: Thread, with arg: Any?, waitUntilDone wait: Bool, modes array: [String]?)
@available(iOS 2.0, *)
open func perform(_ aSelector: Selector, on thr: Thread, with arg: Any?, waitUntilDone wait: Bool)
@available(iOS 2.0, *)
open func performSelector(inBackground aSelector: Selector, with arg: Any?)
}
复制代码
2. Thread的基本使用
Thread的基本使用相当简单,和GCD基本上差不多。也有启动、暂停、取消、阻塞、设置优先级等等。
方法 | 作用 |
start | 启动 |
cancel | 暂停 |
exit | 取消 |
sleep | 阻塞 |
Thread的常用属性
名称 | 用途 |
name | 给线程命名,方便查找 |
stackSize | 栈区大小,看看线程在栈区占了多大空间 |
isMainThread | 是否是主线程 |
qualityOfService | 服务质量,iOS8.0推出,为了取代优先级。 |
- 说明:thread设置了
start
后,其实并不是马上就开始运行了。实质上是放进了可调度线程池,等待被CPU调用。线程执行结束之前,状态可能会在就绪状态 和 运行状态 之间来回的切换 就绪状态 和 运行状态 之间的状态切换由CPU来完成,程序员无法干涉。 - 阻塞:正在运行thread可以通过sleep的方式来阻塞线程的执行。
- 退出:thread在执行完毕之后会自动退出。如果执行了
exit
,线程会强制退出。有几点需要注意一下:
- 不要在主线程中调用啊,会让UI线程退出的。退出之后看你怎么搞!
- 退出之后,这个线程剩下的所有代码都不会被执行。
- 调用这个方法之前,一定要注意释放之前由C语言创建的对象,不然会造成内存泄漏等问题。
- 取消:这个
cancel
和GCD一样的啦,并没有真证的取消线程,只是打了一个标志。取消需要自己的实现。也就是在大人物开始之前,先判断一下这个标志位的状态。要是从来都没写过这个标志位的状态判断,那cancel了也是白瞎。
3. 使用NSCondition实现线程间通讯
这个玩意一共就四个方法,我们索性都来看看:
方法名称 | 作用 |
wait | 使线程处于等待状态 |
wait(until limit: Date) -> Bool | 在给定的时间到达时仍未有信号量出现, 就自动继续了 |
signal | 唤醒线程 |
broadcast | 唤醒所有等待线程 |
NSCondition实现了NSLocking协议,所以它本身也有lock和unlock方法。配合在一起可以解决线程同步的问题,只要在线程开始时加锁,取得资源后释放锁即可。使用时把需要加锁的代码放到lock
和unlock
之间就可以了。
主要不要把什么乱七八糟的都往加锁代码里面放,放在这个里面的应该是抢占资源的读取和修改。不然一个线程执行的时候另一个线程就一直在等待,那还要多线程干哈玩意?!
我们搞个例子来看看。
需求:
1,模拟下载五张图片、五篇文章;
2,图片下载了两张之后,暂停下载,转而开启文章下载;
3,下载三篇文章之后,暂停下载,转而继续下载剩下的三张图片。
4,图片下载完成后,下载完成剩下的两篇文章。
说了是模拟啊,所以下载过程就直接print了噢。
class ViewController: UIViewController {
var downImages: Thread?
var downArticles: Thread?
let imageCondition = NSCondition()
let articleCondition = NSCondition()
override func viewDidLoad() {
super.viewDidLoad()
downImages = Thread(target: self, selector: #selector(downloadImages), object: nil)
downArticles = Thread(target: self, selector: #selector(downloadArticles), object: nil)
downImages?.start()
}
@objc private func threadPrint() {
Thread.sleep(forTimeInterval: 2)
print("After 2 seconds, I have been performed. I am \(Thread.current)")
}
@objc fileprivate func downloadImages() {
for index in 1...5 {
print("Downloading No.\(index) image.")
Thread.sleep(forTimeInterval: 1)
if index == 2 {
//start downArticles.开启下载文章的线程
downArticles?.start()
//Lock the image thread.加锁,让下载图片的线程进入等待状态
imageCondition.lock()
imageCondition.wait()
imageCondition.unlock()
}
}
print("All images have been completed.")
//Signaling the article when all images completed.
// 等图片都下载完成之后,激活下载文章的进程
articleCondition.signal()
}
@objc fileprivate func downloadArticles() {
for index in 1...5 {
print("The No.\(index) article will be downloading.")
Thread.sleep(forTimeInterval: 1)
if index == 3 {
//Signaling the image thread, let it continue to down.
//激活图片的线程,让它继续下载图片
imageCondition.signal()
//Lock the article thread.加锁,让下载文章的线程进入等待状态
articleCondition.lock()
articleCondition.wait()
articleCondition.unlock()
}
}
print("There are 5 articles.")
}
}
复制代码
我们打印一下最终的打印结果:
好了。最后再说一下基本上没人用的pthread
。
4. pthread
其实不知道这个多线程的技术现在还有谁会在用,除了面试可能会偶尔问一下这个名词。 pthread是POSIX thread的简写。表示跨平台的线程接口。
为了能够写Demo,非典型技术宅在浩瀚的Google海洋中游荡了很久,然后,然后,然后放弃了...
怎么能这么不坚持呐,在OC时代曾经还是能写出来创建的。于是去看苹果手册,然后,然后,然后放弃了...
Threading Programming Guide 。这里倒是提到了POSIX thread ,还让人家去看pthread man page
。但是,明显链接被删除了。
苹果爸爸。。。好想锤你小胸口。好吧,放弃了。