NSThread
这套方案是经过苹果封装后的,并且完全面向对象的。所以你可以直接操控线程对象,非常直观和方便。但是,它的生命周期还是需要我们手动管理,所以这套方案也是偶尔用用,比如 NSThread.currentThread()
,它可以获取当前线程类,你就可以知道当前线程的各种属性,用于调试十分方便。
在做定时器的时候也可以用用,只是用多线程启动,并不需要关心线程本身。
创建并启动
- 先创建线程类,再启动
let thread = NSThread(target: self, selector: "run:", object: nil)
//启动
thread.start()```
* 创建并自动启动
NSThread.detachNewThreadSelector("run:", toTarget: self, withObject: nil)
* 使用 NSObject 的方法创建并自动启动
self.performSelectorInBackground("run:", withObject: nil)
>Swift中,这个方法不推荐使用。
##其他方法
//取消线程
public func cancel()
//启动线程
public func start()
//判断某个线程的状态的属性
public var executing: Bool { get }
public var finished: Bool { get }
public var cancelled: Bool { get }
//设置和获取线程名字
public var name: String?
//获取当前线程信息
public class func currentThread() -> NSThread
//获取主线程信息
public class func mainThread() -> NSThread
//使当前线程暂停一段时间,或者暂停到某个时刻
public class func sleepForTimeInterval(ti: NSTimeInterval)
public class func sleepUntilDate(date: NSDate)
#NSOperation和NSOperationQueue
```NSOperation``` 是苹果公司对 ```GCD``` 的封装,完全面向对象。操作步骤:
1. 将要执行的任务封装到一个 ```NSOperation``` 对象中
。
2. 将此任务添加到一个 ```NSOperationQueue``` 对象中
。
##普通用法
//1.创建队列
let queue = NSOperationQueue()
//2.创建NSBlockOperation对象
let operation = NSBlockOperation { () -> Void in
NSLog("%@", NSThread.currentThread())
}
//3.添加多个Block
for i in 0..<5 {
operation.addExecutionBlock { () -> Void in
NSLog("第%ld次 - %@", i, NSThread.currentThread())
}
}
//4.队列添加任务
queue.addOperation(operation)```
多线程环境下,打印输出用
NSLog()
,不用print()
任务依赖
比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了:
//1.任务一:下载图片
let operation1 = NSBlockOperation { () -> Void in
NSLog("下载图片 - %@", NSThread.currentThread())
NSThread.sleepForTimeInterval(1.0)
}
//2.任务二:打水印
let operation2 = NSBlockOperation { () -> Void in
NSLog("打水印 - %@", NSThread.currentThread())
NSThread.sleepForTimeInterval(1.0)
}
//3.任务三:上传图片
let operation3 = NSBlockOperation { () -> Void in
NSLog("上传图片 - %@", NSThread.currentThread())
NSThread.sleepForTimeInterval(1.0)
}
//4.设置依赖
operation2.addDependency(operation1) //任务二依赖任务一
operation3.addDependency(operation2) //任务三依赖任务二
//5.创建队列并加入任务
let queue = NSOperationQueue()
queue.addOperations([operation3, operation2, operation1], waitUntilFinished: false)
从其他线程回到主线程
NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in
}```
#GCD
它是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。
##普通用法
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//这里写需要大量时间的代码
print("这里写需要大量时间的代码")
dispatch_async(dispatch_get_main_queue(), {
//这里返回主线程,写需要主线程执行的代码
print("这里返回主线程,写需要主线程执行的代码")
})
})
## 创建队列
* 主队列
let queue = ispatch_get_main_queue()
* 自己创建的队列
//串行队列
let queue = dispatch_queue_create("tk.bourne.testQueue", nil);
let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL)
//并行队列
let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT)
* 全局并行队列
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
>只要是并行任务一般都加入到这个队列。这是系统提供的一个并发队列。
##创建任务
* 同步任务:会阻塞当前线程 (SYNC)
dispatch_sync(<#queue#>, { () -> Void in
//code here
println(NSThread.currentThread())
})
异步任务:不会阻塞当前线程 (ASYNC)
dispatch_async(<#queue#>, { () -> Void in
//code here
println(NSThread.currentThread())
})
###示例一:
以下代码在主线程调用,结果是什么?
NSLog("之前 - %@", NSThread.currentThread())
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
NSLog("sync - %@", NSThread.currentThread())
})
NSLog("之后 - %@", NSThread.currentThread())
####答案:
只会打印第一句:```之前 - {number = 1, name = main}``` ,然后主线程就卡死了,你可以在界面上放一个按钮,你就会发现点不了了。
####解释:
同步任务会阻塞当前线程,然后把 Block 中的任务放到指定的队列中执行,只有等到 Block 中的任务完成后才会让当前线程继续往下运行。
那么这里的步骤就是:打印完第一句后,```dispatch_sync``` 立即阻塞当前的主线程,然后把 Block 中的任务放到 ```main_queue``` 中,可是 ```main_queue``` 中的任务会被取出来放到主线程中执行,但主线程这个时候已经被阻塞了,所以 Block 中的任务就不能完成,它不完成,```dispatch_sync``` 就会一直阻塞主线程,这就是死锁现象。导致主线程一直卡死。
###示例二:
以下代码会产生什么结果?
let queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL)
NSLog("之前 - %@", NSThread.currentThread())
dispatch_async(queue, { () -> Void in
NSLog("sync之前 - %@", NSThread.currentThread())
dispatch_sync(queue, { () -> Void in
NSLog("sync - %@", NSThread.currentThread())
})
NSLog("sync之后 - %@", NSThread.currentThread())
})
NSLog("之后 - %@", NSThread.currentThread())
####答案:
```2015-07-30 02:06:51.058 test[33329:8793087] 之前 - {number = 1, name = main}
2015-07-30 02:06:51.059 test[33329:8793356] sync之前 - {number = 2, name = (null)}
2015-07-30 02:06:51.059 test[33329:8793087] 之后 - {number = 1, name = main}```
很明显 ```sync - %@``` 和 ```sync之后 - %@``` 没有被打印出来!这是为什么呢?我们再来分析一下:
####分析:
1. 使用 DISPATCH_QUEUE_SERIAL 这个参数,创建了一个 串行队列。
2. 打印出 ```之前 - %@ ```这句。
3. ```dispatch_async``` 异步执行,所以当前线程不会被阻塞,于是有了两条线程,一条当前线程继续往下打印出 ```之后 - %@```这句, 另一条执行 Block 中的内容打印 ```sync之前 - %@``` 这句。因为这两条是并行的,所以打印的先后顺序无所谓。
4. ```dispatch_sync```同步执行,于是它所在的线程会被阻塞,一直等到 ```sync``` 里的任务执行完才会继续往下。于是 ```sync``` 就把自己 Block 中的任务放到 ```queue``` 中,可 ```queue``` 是一个串行队列,一次执行一个任务,所以 ```sync``` 的 Block 必须等到前一个任务执行完毕,可是 ```queue``` 正在执行的任务就是被 ```sync``` 阻塞了的那个。于是又发生了死锁。所以 ```sync``` 所在的线程被卡死了。剩下的两句代码自然不会打印。
## 队列组
队列组可以将很多队列添加到一个组里,这样做的好处是,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。下面是使用方法,这是一个很实用的功能。
//1.创建队列组
let group = dispatch_group_create()
//2.创建队列
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
//3.多次使用队列组的方法执行任务, 只有异步方法
//3.1.执行3次循环
dispatch_group_async(group, queue) { () -> Void in
for _ in 0..<3 {
NSLog("group-01 - %@", NSThread.currentThread())
}
}
//3.2.主队列执行8次循环
dispatch_group_async(group, dispatch_get_main_queue()) { () -> Void in
for _ in 0..<8 {
NSLog("group-02 - %@", NSThread.currentThread())
}
}
//3.3.执行5次循环
dispatch_group_async(group, queue) { () -> Void in
for _ in 0..<5 {
NSLog("group-03 - %@", NSThread.currentThread())
}
}
//4.都完成后会自动通知
dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in
NSLog("完成 - %@", NSThread.currentThread())
}
##从其他线程回到主线程
dispatch_async(dispatch_get_main_queue(), { () -> Void in
})```
延时执行
var time = dispatch_time(DISPATCH_TIME_NOW, (Int64)(10 * NSEC_PER_SEC))
dispatch_after(time, globalQueue) { () -> Void in
println("在10秒后执行")
}
NSEC_PER_SEC
表示的是秒数,它还提供了NSEC_PER_MSEC
表示毫秒。
参考文献
一篇比较好的关于多线程的文章