任务:任务是指执行什么样的操作。
队列:用来存放任务,分为串行队列和并行队列。串行队列:当前任务执行完成之后再执行下一个任务;并行队列:不等待当前任务执行完,下一个任务并发执行
线程:执行任务需要线程,线程从队列中以先进先出(FIFO)的方式取出任务执行,线程一次只能执行一个任务
在GCD中,任务可看作是代码块(block),执行这些任务、同步还是异步执行,由GCD中的调度函数以及队列的类型来决定。
GCD中的队列:
dispatch_main_queue 主队列,程序启动时与主线程一起由系统自动创建,UI操作都放在主队列中
dispatch_get_global_queue 全局并发队列,由系统定义,调用函数dispatch_get_global_queue(identifier: Int, flags: Uint)获取。identifier以前叫做优先级,现在称为服务质量,现在传入优先级的宏定义也可以,目测优先级的界限并不明显,所以平时直接传入0,见下面对应关系。flags是系统保留的一个参数,传入0即可。
DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED 2
DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT 0
DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY -2
DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND INT16_MIN
let queueDefault1 =dispatch_get_global_queue(0,0)
let queueDefault2 = dispatch_get_global_queue(0,0)
let queueHigh3 = dispatch_get_global_queue(2,0)
let queueHigh4 = dispatch_get_global_queue(2,0)
print(queueDefault1)
print(queueDefault2)
print(queueHigh3)
print(queueHigh4)
运行结果:
<OS_dispatch_queue_root: com.apple.root.default-qos[0x112602240]>
<OS_dispatch_queue_root: com.apple.root.default-qos[0x112602240]>
<OS_dispatch_queue_root: com.apple.root.user-initiated-qos[0x1126023c0]>
<OS_dispatch_queue_root: com.apple.root.user-initiated-qos[0x1126023c0]>
dispatch_queue_create(label: UnsafePointer<Int8>, attr: dispatch_queue_attr_t!) 自己创建队列。label是该队列的名字,可以调用dispatch_get_queue_label(queue: dispatch_queue_t!) 获取。attr传入DISPATCH_QUEUE_SERIAL(创建串行队列,可用nil替代)或DISPATCH_QUEUE_CONCURRENT(创建并发队列)
dispatch_queue_create 创建的优先级与全局并发队列的默认优先级是同一级别,也就是说创建的队列的优先级为默认优先级。至于怎么改变它的优先级需要调用dispatch_set_target_queue,这个函数后面描述。
执行任务的调度函数:
dispatch_sync 将任务提交到相应队列,同步执行,不开新线程
dispatch_async 将任务提交到相应队列,异步执行,如果从主队列取任务,不开新线程,在主线程中执行;其他队列,开新线程执行
现在来看看调度函数与各种队列的组合后的情况:
1、dispatch_sync 与 串行队列:
如果,在串行队列queue中调用该函数、且该函数是从串行队列queue中去任务执行,就会出现线程死锁。
例如下面两种情况:
(1)在主线程中调用
dispatch_sync(dispatch_get_main_queue()) { print("在主队列中执行任务") }
(2)在同步执行任务的子线程中调用
let queue = dispatch_queue_create("queue1",DISPATCH_QUEUE_SERIAL)
dispatch_async(queue) {
/*
执行操作
*/
dispatch_sync(queue, {
print("在当前队列中执行操作")
})
}
如果该串行队列与当前队列不同,不会出现线程死锁,在当前线程中同步执行,也就是说此时调用该函数没有意义
2、dispatch_sync 与 并行队列:
因为dispatch_sync不开线程,并且线程一次只能执行一个任务,任务在当前线程中没执行完,下一个任务就没法执行。也就是说,队列中的任务是顺序执行的,此时调用该函数也没有意义。
3、dispatch_async 与 串行队列:
(1)如果是主队列,不开新线程,任务将在主线程中执行。但不会马上执行,而是将任务从栈copy到堆,等待主队列中栈区的任务执行完,再执行堆区的任务
(2)创建的串行队列,开新线程同步执行
4、dispatch_async 与 并发队列:
开新线程并发执行任务,至于开多少条线程由系统决定,例如:
for iin1...20 {
dispatch_async(dispatch_get_global_queue(0,0)) {
print("线程\(NSThread.currentThread())执行第\(i)个任务")
}
}
运行效果:
线程<NSThread: 0x7fab94900570>{number = 6, name = (null)}执行第8个任务
线程<NSThread: 0x7fab94900850>{number = 8, name = (null)}执行第3个任务
线程<NSThread: 0x7fab926099e0>{number = 3, name = (null)}执行第2个任务
线程<NSThread: 0x7fab9270b5f0>{number = 12, name = (null)}执行第11个任务
线程<NSThread: 0x7fab94b00260>{number = 4, name = (null)}执行第6个任务
线程<NSThread: 0x7fab94900400>{number = 2, name = (null)}执行第5个任务
线程<NSThread: 0x7fab9261c6e0>{number = 9, name = (null)}执行第1个任务
线程<NSThread: 0x7fab94a01cc0>{number = 7, name = (null)}执行第7个任务
线程<NSThread: 0x7fab92416080>{number = 14, name = (null)}执行第13个任务
线程<NSThread: 0x7fab92415ac0>{number = 5, name = (null)}执行第4个任务
线程<NSThread: 0x7fab92714400>{number = 13, name = (null)}执行第12个任务
线程<NSThread: 0x7fab92415ec0>{number = 10, name = (null)}执行第9个任务
线程<NSThread: 0x7fab926280b0>{number = 11, name = (null)}执行第10个任务
线程<NSThread: 0x7fab94900570>{number = 6, name = (null)}执行第14个任务
线程<NSThread: 0x7fab94900850>{number = 8, name = (null)}执行第15个任务
线程<NSThread: 0x7fab924160c0>{number = 15, name = (null)}执行第16个任务
线程<NSThread: 0x7fab926099e0>{number = 3, name = (null)}执行第17个任务
线程<NSThread: 0x7fab9270b5f0>{number = 12, name = (null)}执行第18个任务
线程<NSThread: 0x7fab94b00260>{number = 4, name = (null)}执行第20个任务
线程<NSThread: 0x7fab9241f490>{number = 16, name = (null)}执行第19个任务
let group =dispatch_group_create()
let queue =dispatch_get_global_queue(0,0)
dispatch_group_async(group, queue) {
print("下载小说A")
}
dispatch_group_async(group, queue) {
print("下载小说B")
}
dispatch_group_async(group, queue) {
print("下载小说C")
}
dispatch_group_notify(group,dispatch_get_main_queue()) {
print("小说下载完成")
}
下载小说C
下载小说A
下载小说B
小说下载完成
也可将dispatch_group_notify(group, dispatch_get_main_queue()) { print("小说下载完成")} 换成
let flag = dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
if flag == 0 {
print("小说下载完成")
} else {
print("小说下载超时")
}
dispatch_once 一次性操作 调用函数 dispatch_once(predicate: UnsafeMutablePointer<dispatch_once_t>, block: dispatch_block_t)
参数predicate 用来标识block是否执行的指针,必须是全局或静态变量,并且该指针所指向的区域的初始值为0,当任务执行完毕,系统会将其值置为-1
这个函数用于初始化全局数据,并且保证线程安全,常用于OC中的单例模式
dispatch_apply 结合dispatch_sync 与 dispatch_group 的函数 调用 dispatch_apply(iterations: Int, queue: dispatch_queue_t, block: (Int)->Void)
dispatch_async(queue) {
dispatch_apply(arr.count,dispatch_get_global_queue(0,0)) { (i) in
print(arr[i])
}
print("done")
}
dispatch_barrier_async
在处理数据读取时,为了避免数据竞争的问题,写入操作与写入操作或读取操作不能并发执行。针对这个例子,可以使用dispatch_barrier_async将写入操作提交到并发队列中,此时被提交的任务暂不执行,当他前面的任务执行完毕时在执行该任务,等到该任务执行完毕,后面提交的才执行执行。需要注意的是指定的queue应该是通过dispatch_queue_create创建的,系统定义的全局并发队列无效。示例代码如下:
let queue =dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT)
dispatch_async(queue) {print("读取操作1") }
dispatch_async(queue) {print("读取操作2") }
dispatch_async(queue) {print("读取操作3") }
dispatch_barrier_async(queue) {print("写入操作1") }
dispatch_async(queue) {print("读取操作4") }
dispatch_async(queue) {print("读取操作5") }
dispatch_async(queue) {print("读取操作6") }
dispatch_barrier_async(queue) {print("写入操作2") }
dispatch_async(queue) {print("读取操作7") }
dispatch_async(queue) {print("读取操作8") }
读取操作2
读取操作1
读取操作3
写入操作1
读取操作5
读取操作4
读取操作6
写入操作2
读取操作7
读取操作8
这个函数的具体作用是这样的,如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1,返回值为0;如果desema的值为0,那么这个函数就阻塞当前线程等待timeout,如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,且该函数所处线程获得了信号量,那么就继续向下执行并将信号量减1, 返回值为0。如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句,返回值大于0。通过返回值是否为0,可以判断等待是否超时。
let semaphore =dispatch_semaphore_create(1)
var arr = [Int]()
for i in 0...100 {
dispatch_async(dispatch_get_global_queue(0,0))
/*
其他并发操作
*/
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER)
/*
同步操作,例如 arr.append(i)
*/
arr.append(i)
dispatch_semaphore_signal(semaphore)
/*
其他并发操作
*/
}
}
let queue =dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT)
dispatch_set_target_queue(queue,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0))
函数中第一个是需要变更优先级的队列,第二个参数是需要传入全局并发队列,并且优先级是第一个参数想要的优先级。
(2)修改用户队列的目标队列,使多个serial queue在目标queue上一次只有一个执行。第二个参数是目标队列,第一个参数是任务将放在目标队列中的另一串行队列。
例如,用户要依次下载小说A、B、C,但下载任务放在不同的串行队列中,这时就可以依次调用dispatch_set_target_queue,将放有下载任务的队列作为第一个参数传入,让任务将目标队列中同步执行。 示例代码如下:
let targetQueue = dispatch_queue_create("targetQueue",DISPATCH_QUEUE_SERIAL)
let queue1 = dispatch_queue_create("queue1",DISPATCH_QUEUE_SERIAL)
let queue2 = dispatch_queue_create("queue2",DISPATCH_QUEUE_SERIAL)
let queue3 = dispatch_queue_create("queue3",DISPATCH_QUEUE_SERIAL)
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue);
dispatch_async(queue1) {
print("开始下载小说A")
/*
下载中
*/
print("小说A下载完成")
}
dispatch_async(queue1) {
print("开始下载小说B")
/*
下载中
*/
print("小说B下载完成")
}
dispatch_async(queue1) {
print("开始下载小说C")
/*
下载中
*/
print("小说C下载完成")
}
运行结果:
开始下载小说A
小说A下载完成
开始下载小说B
小说B下载完成
开始下载小说C
小说C下载完成