遐想
即胡思乱想,编程从来都是向前看,所以不考虑switf3以前
同步
阻塞当前线程
异步
不阻塞当前线程
warning
:
1.不能用同步和异步是否能开启线程来区别,跟是否具有开启线程的能力没有半毛钱关系
下面是2种线程阻塞的场景
(串行队列强行同步执行)
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.sync {
print("这里会阻塞死亡,永远都不会执行")
} //主队列是串行队列,里面任务是依次执行。
//这里开辟一个同步任务,执行这个任务就要先把前面的任务执行完毕。
//但是同步任务需要立刻执行,就会造成阻塞。
}
serialQueue.async {//serialQueue串行队列,这里不管同步(sync)还是异步(async)效果都一样
print("\(1)\(Thread.current)")
self.serialQueue.sync {
print("it is main\(Thread.current)
}
} //阻塞现象同上
GCD
遐想:GCD内部应该维持着一个线程池子(线程的多少得益于程序运行环境)。
还维护着多种队列:
1.主队列
DispatchQueue.main//获取主队列,串行队列
2.全局队列,都是并行队列Qos(quality of service)
public enum QoSClass {
case background //后台
case utility //周期性的用户请求,比如:定时检查新消息
case `default` //这个看似这么特殊的默认,最好不用,默认就用默认参数吧
case userInitiated //用户期望(用于请求的任务,不太耗时的操作)
case userInteractive //用户交互(与图形处理相关的任务,比如动画)
case unspecified //意思是未说明的,那就不用吧
}
//获取全局队列
let globleQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.background)
3.自定义队列
let serialQueue = DispatchQueue(label: "serialQueue") //串行队列
let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: DispatchQueue.Attributes.concurrent) //并行队列
GCD对队列的维护:
1.只要队列中有任务,GCD就会从线程池中调度线程来执行
2.主队列,只会调度主线程来执行(主线程和主队列2个是绑定的)
3.其他队列,会调度其他线程来处理任务
同步 | 异步 | |
---|---|---|
串行队列 | 当前线程,一个一个执行 | 任意线程,一个一个执行 |
并行队列 | 当前线程,一个一个执行 | 多个线程,同时执行 |
warning:
遐想:上面表格的现象,只是实践后的总结,理论上:任何任务都不能确定是在那个线程上执行的。
线程切换是要消耗一定的资源的,居于这个考虑,以下推断
a.串行队列
:已经决定任务的执行顺序,所以串行队列不管同步
还是异步
都只需要调度一个线程就可以了.
同步:
GCD不想浪费线程切换的资源,就直接使用当前线程
异步:
GCD会从线程池里面随便调度一个线程来执行这个队列的任务,执行完队列的全部任务,就会放回线程池,等待调度
b.并行队列
:里面的任务没有执行顺序
同步:
GCD不想浪费线程切换的资源,就直接使用当前线程
异步:
GCD会根据当前系统资源最优化的调度不定数(>=0,理论上没有可调度的线程的时候只有等待了)的线程来处理任务
串行队列,同步
@IBAction func action1(_ sender: Any) {
for i in 1...1000{
serialQueue.sync {
print("\(i)\(Thread.current)")
}
}
print("it is main\(Thread.current)")
}
999{number = 1, name = main}
1000{number = 1, name = main}
it is main{number = 1, name = main}
串行队列,异步
@IBAction func action1(_ sender: Any) {
for i in 1...1000{
serialQueue.async {
print("\(i)\(Thread.current)")
}
}
}
第一次点击:
999{number = 3, name = (null)}
1000{number = 3, name = (null)}
第二次点击:
999{number = 4, name = (null)}
1000{number = 4, name = (null)}
//每次调度的都是不同的线程来执行,但是不保证一定不是。一切都是资源优化来调度的
并行队列,同步
@IBAction func action1(_ sender: Any) {
for i in 1...1000{
concurrentQueue.sync {
print("\(i)\(Thread.current)")
}
}
print("it is main\(Thread.current)")
}
999{number = 1, name = main}
1000{number = 1, name = main}
it is main{number = 1, name = main}
并行队列,异步
@IBAction func action1(_ sender: Any) {
for i in 1...1000{
concurrentQueue.async {
print("\(i)\(Thread.current)")
}
}
print("it is main\(Thread.current)")
}
113{number = 11, name = (null)}
114{number = 13, name = (null)}
it is main{number = 1, name = main}
115{number = 13, name = (null)}
遐想:串行队列,不管同步异步,每次(每次,指的是:串行队列中任务的一次执行,一次执行是将里面的任务执行完毕。当加入新任务后,算下一次了,GCD将另外从线程池里调度,调度规则还是同步就是当前线程,异步就是随便调度一个线程)都只有一个线程为其服务。同步就是当前线程,异步就是从调度池里随便调度一个
异步并行队列
这情况就不说了,简直乱执行嘛(这就是想要的结果)
延迟执行
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2){
//do something
}
DispatchWorkItem
将上面的
block
封装成一个taskid
let taskID = DispatchWorkItem {
print("workItem is working:\(Thread.current)"
}
concurrentQueue.async(execute: taskID)//将任务放入队列
item.wait()//等待,等任务执行完成才继续后面的行为
item.wait(timeout: DispatchTime.now() + 4) //最多等待多少秒,就继续执行下面的行为
item.cancel() //在任务还未执行之前,可以取消该任务
item.perform() //直接在当前线程执行block里面的任务,不会触发notify监听
item.notify(queue: serialQueue){ //和队列组一样的通知,多次测试,执行这个通知的线程和执行任务的线程是一致的()
print("workitem haved doing:\(Thread.current)")
}
使用
let item = DispatchWorkItem {
print("workItem is working:\(Thread.current)")
}
serialQueue.async {
sleep(5)
print("sleep 5")
}
serialQueue.async(execute: item)
print("before working")
item.cancel()
item.wait()//等待
item.notify(queue: serialQueue){
print("workitem haved doing:\(Thread.current)")
}
print("after working")
输出:
before working
sleep 5
after working
workitem haved doing:{number = 3, name = (null)}
mark:item.cancel()
并不能影响到item.wait()
和item.notify()
队列组
将一堆任务化为一个组,并可以对组进行完成监听,或者阻塞,完成后在继续执行,
let dispatchGroup = DispatchGroup()
dispatchGroup.notify(queue: concurrentQueue){}//需要放在最后,否则会有问题(按照这个怪咧来就是)
for i in 1...100{
serialQueue.async(group: dispatchGroup){
print("串行队列\(i):\(Thread.current)")
}
}
for i in 1...100{
concurrentQueue.async(group: dispatchGroup){
print("并行队列\(i):\(Thread.current)")
}
}
dispatchGroup.notify(queue: concurrentQueue){//队列组任务结束,会收到通知
print("执行完成2:\(Thread.current)")
}
print("顺利执行等待前:\(Thread.current)")
dispatchGroup.wait()//阻塞后面代码的执行,直到队列组任务的完成
print("顺利执行等待后:\(Thread.current)")
输出:
顺利执行等待前:{number = 1, name = main}
串行队列1:{number = 3, name = (null)}
串行队列2:{number = 3, name = (null)}
并行队列1:{number = 4, name = (null)}
.
.
.
并行队列100:{number = 44, name = (null)}
.
.
.
串行队列100:{number = 3, name = (null)}
顺利执行等待后:{number = 1, name = main}
执行完成2:{number = 44, name = (null)}
信号量
遐想:最主要的就是用于对资源上锁。当信号设置为1的话
let semaphore = DispatchSemaphore(value: 1)
semaphore.wait() //-1
semaphore.signal() //+1
Dispatch Barrier(栅栏)
只针对一个
并行队列
同步点
之前的任务,会并发执行,到了同步点就会等待,等待同步点的任务执行完成的时候,继续后面的任务,再次并发执行
let writeTask = DispatchWorkItem(flags: DispatchWorkItemFlags.barrier) { //同步点
print("当前只会有一个线程在服务:\(Thread.current)")
}
for i in 0..<100 {
if i == 50 {
concurrentQueue.async(execute: writeTask)
}else{
let readTask = DispatchWorkItem {
print("\(i):这里绝对的并发:\(Thread.current)")
}
concurrentQueue.async(execute: readTask)
}
}
}
输出:
48:这里绝对的并发:{number = 40, name = (null)}
49:这里绝对的并发:{number = 30, name = (null)}
当前只会有一个线程在服务:{number = 30, name = (null)}
51:这里绝对的并发:{number = 40, name = (null)}
52:这里绝对的并发:{number = 30, name = (null)}
//当然这里不一定是这么个顺序:48、49、同步任务、51、52。
//但一定是先执行了前面50个任务,然后等待`同步点`执行,再执行后面49个任务。