iOS多线程 - NSThread

首先介绍 NSThread 创建线程的方法:

1
public convenience init(target: AnyObject, selector: Selector, object argument: AnyObject?)

public class NSThread : NSObject 从这里可以看出,NSThread 是继承自 NSObject 的类,它的创建是一个构造器 init ,所以使用实例化对象的方式就可以调用了。

1
let thread = NSThread(target: <AnyObject>, selector: <Selector>, object: <AnyObject>)

现在我们来看一下,这个方法的几个参数,分别代表的含义。

形参名 形参类型 作用 方法填写
target AnyObject 当前类的一个对象。是selector消息发送的对象。 一般是 self
selector Selector 线程要执行的方法。只能接收一个参数。 创建方法传入
object AnyObject? selector线程函数的唯一传入值,可以是 nil 。 nil

下面,我们要做的是通过点击,来调用 NSThread

我们在ViewController里创建一个touchesBegan的方法,并创建一个对象接收线程:

1
2
3
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let thread = NSThread(target: self, selector: #selector(ViewController.run(_:)), object: "我是run的传入值")
}

然后我们在ViewController 中创建 run 这个方法:

1
2
3
4
5
//线程函数
func run(str: NSString) {
let thread = NSThread.currentThread()
print("\(str) , 我是线程\(thread) , 我要run了")
}

touchesBegan 方法中,启动线程:

1
2
3
4
5
6
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//创建线程
let thread = NSThread(target: self, selector: #selector(ViewController.run(_:)), object: "我是run的传入值")
//启动线程
thread.start()
}

现在我们运行模拟器,点击一下屏幕,控制台打印出:

1
我是run的传入值 , 我是线程<NSThread: 0x7b97e1c0>{number = 2, name = (null)} , 我要run了

number = 2, name = (null) 所以我们成功使用了NSThread 子线程。

我是run的传入值,表示 init 构造器的第三个参数,同时也是第二个参数的传入值。


isMainThread mainThread

这三个的表达式如下

1
2
3
4
5
public var isMainThread: Bool { get }
public class func isMainThread() -> Bool
public class func mainThread() -> NSThrea

我们来看一下这三个表达式的含义

名字 类型 作用
var isMainThread 计算属性,返回 bool 类型。 判断一条线程是不是主线程
func isMainThread 类型方法,返回 bool 类型。 判断一条线程是不是主线程
func mainThread 类型方法, 返回线程类型。 返回当前线程里的主线程

现在我们来使用 var isMainThread 计算属性做一个判断,我们在 TouchesBengan 方法中写入:

1
2
3
4
5
6
if NSThread.currentThread().isMainThread {
print("touchesBegan 是主线程")
}
else {
print("touchesBegan 不是主线程")
}

run 方法中写入:

1
2
3
4
5
6
if NSThread.currentThread().isMainThread {
print("run 是主线程")
}
else {
print("run 不是主线程")
}

运行程序,点击屏幕,控制台输出:

1
2
touchesBegan 是主线程
run 不是主线程

通过 isMainThread 我们可以得知当前方法所在的线程。

接下来,我们通过 mainThread 来获取主线程,首先我们在 run 这个方法中写入 :

1
2
3
4
5
6
if NSThread.mainThread().isMainThread {
print("NSThread.mainThread 是主线程")
}
else {
print("NSThread.mainThread 不是主线程")
}

运行程序,点击屏幕,控制台输出:

1
NSThread.mainThread 是主线程

我们在子线程中获得了主线程,可以说明, func mainThread 返回的是一个主线程。


栈大小 stackSize

下面是它的表达式:

1
public var stackSize: Int

由此可以看出,stackSize 是一个储存属性的整型。

每个线程在创建时都会分配一块内存空间,这个内存空间叫做自有栈,下面我们来看下,一个线程的栈是多少,我们在 TouchesBengan 方法中写入:

1
2
print("子线程 的栈大小是\(thread.stackSize/1024)")
print("主线程 的栈大小是\(NSThread.mainThread().stackSize/1024)")

因为 stackSize 传回的是字节数,所以我们 /1024 是它转为 KB,打印结果如下:

1
2
thread 的栈大小是512
thread 的栈大小是512

由此可以看出,主线程和子线程的栈大小均为 512KB。值得注意的是,stackSize 并不是一个只读属性,可以进行赋值,我们在 TouchesBengan 中写入:

1
2
thread.stackSize = 1024*512*100
NSThread.mainThread().stackSize = 1024*512*100

打印结果为:

1
2
子线程 的栈大小是51200
主线程 的栈大小是51200

这个结果相当灵异,栈的大小是否真的有了变化,还需要进一步考证。


线程的优先级

表达式如下:

1
2
3
public var threadPriority: Double
public class func setThreadPriority(p: Double) -> Bool
public class func threadPriority() -> Double

先来看下默认的优先级:

1
print("子线程优先级是\(thread.threadPriority) 主线程优先级是\(NSThread.mainThread().threadPriority)")

打印结果为:

1
子线程优先级是0.5 主线程优先级是0.758064516129032

优先级是CUP处理线程时的权重,同等条件下,优先级越高的线程,优先获得CUP的处理时间。为了验证这一点,我们再创建一个线程。在 TouchesBengan 方法中,写入以下代码:

1
2
3
4
5
let subThread = NSThread(target: self, selector: #selector(ViewController.run(_:)), object: "线程2参数")
subThread.start()
thread.name = "子线程1"
subThread.name = "子线程2"

现在我们有了两条线程,两个线程共同执行 run 这个方法,在这个方法中我们使用 for 循环输出一句话:

1
2
3
4
5
for _ in 0...100 {
print("当前线程是\(NSThread.currentThread().name)
当前参数是\(str)
当前优先级是\(NSThread.currentThread().threadPriority)")
}

运行,控制台输出如下:

1
2
3
4
当前线程是Optional("子线程2") 当前参数是线程2参数
当前线程是Optional("子线程1") 当前参数是线程1参数
当前线程是Optional("子线程2") 当前参数是线程2参数
当前线程是Optional("子线程1") 当前参数是线程1参数

可以看出,在优先级相同的情况下,两个线程轮流执行。现在我们修改线程的优先级:

1
2
subThread.threadPriority = 0.1
thread.threadPriority = 0.9

输出结果为:

1
2
3
4
5
6
7
8
9
10
当前线程是Optional("子线程1") 当前参数是线程1参数
当前线程是Optional("子线程1") 当前参数是线程1参数
当前线程是Optional("子线程1") 当前参数是线程1参数
当前线程是Optional("子线程1") 当前参数是线程1参数
当前线程是Optional("子线程1") 当前参数是线程1参数
当前线程是Optional("子线程1") 当前参数是线程1参数
当前线程是Optional("子线程2") 当前参数是线程2参数
当前线程是Optional("子线程1") 当前参数是线程1参数
当前线程是Optional("子线程2") 当前参数是线程2参数
当前线程是Optional("子线程1") 当前参数是线程1参数

可以看到,刚开始运行时,基本都是权重高的进程在计算,需要注意的是,优先级要设定在另一个进程 start 之前,写在 start 之后无效。


其他属性和方法
1
2
3
4
public class func currentThread() -> NSThread //获取当前的线程
public class func sleepForTimeInterval(ti: NSTimeInterval) //休眠到指定的时间
public class func exit() //退出进程
public class func sleepUntilDate(date: NSDate) //休眠到指定的日期