引言
在多线程编程中,线程是并发执行任务的基本单元。Swift 提供了几种创建和管理线程的方式,其中之一是使用 NSThread。本文将详细探讨 NSThread 的使用、其内部工作原理以及相关的示例代码。
什么是 NSThread?
NSThread 是 Apple 提供的用于在 macOS 和 iOS 平台上创建和管理线程的一个类。虽然 Swift 引入了更现代的并发编程方式(如 GCD 和 OperationQueue),但了解 NSThread 仍然对理解低级线程管理和历史代码有帮助。
NSThread 的基本使用
NSThread 的基本用法非常简单,你可以通过实例化一个 NSThread 对象并调用 start() 方法来启动一个新线程。以下是一个简单的例子:
-
block形式
// 执行后台任务
func doSomeBackgroundTask() {
Thread.sleep(forTimeInterval: 1.0)
print(Thread.current)
}
// 创建线程
let thread1 = Thread.init {
doSomeBackgroundTask()
}
// 启动线程
thread1.start()
-
Selector形式
/// 传递参数
class Client {
@objc class UserInfo: NSObject {
var name: String
init(name: String) {
self.name = name
}
}
init() {
let userInfo = UserInfo(name: "thread")
// 传递参数
let thread = Thread(target: self, selector: #selector(doSomeBackgroundTask(userInfo:)), object: userInfo)
thread.name = "selector thread"
thread.start()
}
@objc func doSomeBackgroundTask(userInfo: UserInfo) {
print(userInfo.name)
Thread.sleep(forTimeInterval: 1.0)
print(Thread.current)
}
}
-
自定义Thread
//自定义Thread
class MyThread: Thread {
// 重写main()函数,执行后台任务
override func main() {
doSomeBackgroundTask()
}
private func doSomeBackgroundTask() {
Thread.sleep(forTimeInterval: 1.0)
print(Thread.current)
}
}
let thread = MyThread()
thread.start()
线程保活
线程保活是指维持线程的存活状态,使其在需要时可以重新执行任务。保持线程在某些需要长时间运行任务的场景中非常有用。
-
通过Runloop保持线程存活
// 注意:aliveThread 应该是一个全局变量
// var aliveThread: AliveThread!
// aliveThread = AliveThread()
// aliveThread.start()
class AliveThread: Thread {
override func main() {
let runloop = RunLoop.current
runloop.add(NSMachPort(), forMode: .default)
runloop.run()
}
@objc func action() {
// 模拟耗时任务
Thread.sleep(forTimeInterval: 1.0)
print("receice, do work\(Thread.current)")
}
// 调用perfrom(), 可以在子线程中执行action()中的任务
func perform() {
perform(#selector(action), on: self, with: nil, waitUntilDone: true)
}
}
-
通过 While 循环
class FileMonitorThread: NSObject {
private var thread: Thread!
private var isRunning = true
private var filePath: String
init(filePath: String) {
self.filePath = filePath
super.init()
thread = Thread(target: self, selector: #selector(threadEntryPoint), object: nil)
thread.start()
}
// 通过while循环线程保活,每隔两秒检查文件是否存在
@objc private func threadEntryPoint() {
while isRunning {
monitorFile()
Thread.sleep(forTimeInterval: 2.0) // 每 2 秒检查一次
}
}
private func monitorFile() {
// 假设我们检查文件是否存在并打印结果
let fileExists = FileManager.default.fileExists(atPath: filePath)
if fileExists {
print("File exists at path: \(filePath)")
} else {
print("File does not exist at path: \(filePath)")
}
}
func stopThread() {
isRunning = false
}
}
实战: 生产-消费者
-
Thread 实现
class ProducerConsumer {
private var buffer: [Int] = []
private let condition = NSCondition()
private let bufferSize = 10
func produce() {
for i in 0..<20 {
condition.lock()
while buffer.count == bufferSize {
condition.wait()
}
buffer.append(i)
print("Produced:\(i)")
condition.signal()
condition.unlock()
Thread.sleep(forTimeInterval: 0.1)
}
}
func consume() {
for _ in 0..<20 {
condition.lock()
while buffer.isEmpty {
condition.wait()
}
let item = buffer.removeFirst()
print("Consumed:\(item)")
condition.signal()
condition.unlock()
Thread.sleep(forTimeInterval: 0.1)
}
}
}
-
await/async 实现
//await/async
actor ProducerConsumer {
private var buffer: [Int] = []
private let bufferSize = 10
func produce() async {
for i in 0..<20 {
while buffer.count == bufferSize {
// 让出线程
await Task.yield()
}
buffer.append(i)
print("Produced: \(i)")
try? await Task.sleep(nanoseconds: UInt64(0.1 * Double(NSEC_PER_SEC)))
}
}
func consume() async {
for _ in 0..<20 {
while buffer.isEmpty {
// 让出线程
await Task.yield()
}
let item = buffer.removeFirst()
print("Consumed: \(item)")
try? await Task.sleep(nanoseconds: UInt64(0.1 * Double(NSEC_PER_SEC)))
}
}
}
结论
自己创建线程使得用户可以更精细的操控线程,但是他们会增加代码的不确定性。线程是一种相对底层且复杂的并发模型,掌握线程知识可以为GCD,Operation Queue,await/asyc 打下坚实基础。