Dispatch Queue,执行处理的等待队列。
Serial Dispatch Queue,等待现在执行中处理。
queue为Serial Dispatch Queue时,因为要等待现在执行中的处理结束,所有首先执行blk0,blk0执行结束后,执行blk1。。。同时执行的处理数只能有1个。
Concurrent Dispatch Queue,不等待现在执行中处理。
queue为Concurrent Dispatch Queue时,因为不用等待现在执行中的处理结束,所以首先执行blk0,不管blk0执行是否结束,都开始执行后面blk1,不管blk1是否执行结束,都开始执行后面的blk2。这样虽然不用等待处理结束,可以并行执行多个处理,但并行执行的处理数量取决于当前系统的状态。即IOS和OS X基于Dispatch Queue中的处理数、CPU核数以及CPU负荷等当前系统的状态来决定Concurrent Dispatch Queue中并行执行的处理数。
Dispatch_queue_create
通过该函数生成Dispatch Queue。虽然Serial Dispatch Queue和Concurrent Dispatch Queue受到系统资源的限制,单用dispatch_queue_create函数可以生成任意多个Dispatch Queue。虽然“Serial Dispatch Queue比Concurrent Dispatch Queue能生成更多的线程”,但如果过多使用多线程,就会消耗大量内存,引起大量的上下文切换,大幅度降低系统的相应性能。
Dispatch_queue_Create函数的返回值为表示Dispatch Queue的“dispatch_queue_t”类型。
通过create函数生成的Dispatch Queue在使用结束后通过dispatch_release函数释放。
在dispatch_async函数中追加Block到Dispatch Queue,换言之,该Block通过dispatch_retain函数持有了Dispatch Queue。无论Serial Dispatch Queue还是Concurrent Dispatch Queue都一样,一旦Block执行结束,就通过dispatch_release函数释放该Block持有的Dispatch Queue。
在通过函数或方法获取Dispatch Queue以及其他名称中有create的API生成的对象时,有必要通过dispatch_retain函数持有,并在不需要时通过release函数释放。
var mySerialDispatchQueue = dispatch_queue_create("DP-K.mySerialDispatchQueue", DISPATCH_QUEUE_SERIAL)
var myConcurrentDispatchQueue = dispatch_queue_create("DP-K.myConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT)
dispatch_async(mySerialDispatchQueue) { () -> Void in
}
dispatch_async(myConcurrentDispatchQueue) { () -> Void in
}
第二种方法是获取系统标准提供的Dispatch Queue。
Main Dispatch Queue,主线程中执行的Dispatch Queue。主线程只有1个,所以Main Dispatch Queue自然就是Serial Dispatch Queue。追加到Main Dispatch Queue的处理在主线程RunLoop中执行。因此要将用户界面的界面更新等一些必须在主线程中执行的处理追加到Main Dispatch Queue使用。
Global Dispatch Queue,所有应用程序都能够使用的Concurrent Dispatch Queue。没有必要通过create函数逐个生成Concurrent Dispatch Queue,只要通过Global Dispatch Queue使用即可。Global Dispatch Queue有4个执行优先级。
但是通过XNU内核用于Global Dispatch Queue的线程不能保证实时性,因此执行优先级知识大致的判断。
var mainDispatchQueue = dispatch_get_main_queue()
var globlDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
var globlDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
var globlDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
var globlDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
对Main Dispatch Queue和Global Dispatch Queue执行retain和release函数也不会引起任何变化。
dipatch_set_target_queue
create函数生成的queue不管是Serial 还是 Concurrent,都是用默认优先级Global Dispatch Queue相同执行优先级的线程。
dispatch_set_target_queue(mySerialDispatchQueue, globlDispatchQueueBackground)
指定要变更执行优先级的Dispatch Queue为dispatch_set_target_queue函数的第一个参数,指定与要使用的执行优先级相同优先级的Global Dispatch Queue为第二个参数(目标)。
在必须将不可并行执行的处理追加到多个Serial Dispatch Queue中时,使用dispatch_set_target_queue函数将目标指定为某一个Serial Dispatch Queue,即可防止处理并行执行。(原本应并行执行的多个Serial Dispatch Queue,在目标Serial Dispatch Queue上只能同时执行一个处理)
dispatch_after
并不是在指定时间后执行处理,而只是在指定时间后追加处理到Dispatch Queue,因为Main Dispatch Queue在主线程RunLoop中执行,所以在比如每隔1/60秒执行的RunLoop中,Block最快在3秒后执行,最慢在3秒+1/60秒后执行,并且在Main Dispatch Queue有大量处理追加或主线程本身有延迟时,这个时间会更长。
var time = dispatch_time(DISPATCH_TIME_NOW, 3 * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) { () -> Void in
}
第一个参数是指定时间用的dispatch_time_t类型的值,使用dispatch_time函数或者dispatch_walltime函数生成。
dispatch_time函数能过获取从第一个参数dispatch_time_t类型值中指定的时间开始,到第二个参数指定的毫微秒单位时间后的时间。第一个参数经常使用DISPATCH_TIME_NOW(现在的时间)。
dispatch_walltime函数用于计算绝对时间。例如dispatch_after函数中想指定2011年11月11日11时11分11秒这一绝对时间。可作为粗略的闹钟功能。
数值和NSEC_PER_SEC的乘积得到单位为毫微秒的数值。ull是C语言的数值字面量(unsigned long long)。
Dispatch Group
在追加到Dispatch Queue中的多个处理全部结束后想执行结束处理。
var queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
var group = dispatch_group_create()
dispatch_group_async(group, queue) { () -> Void in
}
dispatch_group_notify(group, dispatch_get_main_queue(),{() -> Void in
})
无论向什么样的Dispatch Queue中追加处理,使用Dispatch Group都可监视这些处理执行的结束。一旦检测到所有处理执行结束,就可将结束的处理追加到Dispatch Queue中。
在追加到Dispatch Group中的处理全部执行结束时,该源代码中使用的dispatch_group_notify函数会将执行的Block追加到Dispatch Queue中,将第一个参数指定为要监视的Dispatch Group,将第三个参数的Block追加到第二个参数的Dispatch Queue中。在dispatch_group_notify函数中,不管指定什么样的Dispatch Queue,属于Dispatch Group的全部处理在追加指定的Block时都已执行结束。
dispatch_group_wait
第二个参数指定为等待时间(超时),他属于dispatch_time_t类型的值。DISPATCH_TIME_FOREVER意味着永久等待。只要属于Dispatch Group的处理尚未执行结束,就会一直等待,中途不能取消。
如同dispatch_after函数说明中出现的那样,指定等待间隔为1秒时。
var result = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 1 * Int64(NSEC_PER_SEC)))
if result == 0 {
//全部执行结束
}else {
//某个处理还在执行
}
如果返回值不为0,就意味着虽然过了指定时间,但属于Dispatch Group的某一个处理还在执行。如果返回值为0,那么全部处理执行结束。
在主线程的RunLoop的没吃循环中,可检查执行是否结束,从而不耗费多余的等待时间。
dispatch_barrier_async
为了高效率的进行访问,读取处理追加到Concurrent Dispatch Queue,写入处理在任一个读取处理没有执行的状态下,追加到Serial Dispatch Queue中即可。(在写入处理结束之前,读取处理不可执行)
dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue上的并行执行的处理全部结束之后,再将指定的处理追加到该Concurrent Dispatch Queue中。然后在由dispatch_barrier_async函数追加的处理执行完毕之后,Concurrent Dispatch Queue才恢复为一般的动作,追加到该Concurrent Dispatch Queue的处理又开始并行执行。
dispatch_async(myConcurrentDispatchQueue) { () -> Void in
//reading
}
dispatch_async(myConcurrentDispatchQueue) { () -> Void in
//reading
}
dispatch_barrier_async(myConcurrentDispatchQueue) { () -> Void in
//writing
}
dispatch_async(myConcurrentDispatchQueue) { () -> Void in
//reading
}
同步处理,一旦调用dispatch_sync函数,那么在指定的处理执行结束之前,该函数不会返回。
dispatch_barrier_sync函数的作用是在等待追加处理全部执行结束后,再追加处理到Dispatch Queue中,此外,他还域dispatch_sync函数相同,会等待追加处理的执行结束。
dispatch_apply
该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束。
dispatch_apply也会等待处理执行结束,因此推荐在dispatch_async函数中非同步地执行dispatch_apply函数。
dispatch_async(globlDispatchQueueDefault) { () -> Void in
dispatch_apply(10, globlDispatchQueueDefault, { (size_t index) -> Void in
print("\(index)/n")
})
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//用户界面更新
})
}
dispatch_suspend/dispatch_resume
当追加大量处理到Dispatch Queue中,在追加处理的过程中,有时希望不执行已追加的处理。例如结果被Block截获时,一些处理会对这个结果造成影响。这个时候使用dispatch_suspend函数挂起指定的Dispatch Queue。
dispatch_resume函数恢复指定的Dispatch Queue。
挂起后,追加到Dispatch Queue中但尚未执行的处理在此之后停止执行,而恢复则使得这些处理能够继续执行。
Dispatch Semaphore
Dispatch Semaphore是持有计数的信号,该计数是多线程编程中的计数类型信号。计数为0时等待,大于等于1时,减去1不等待。
dispatch_semaphore_wait函数等待Dispatch Semaphore的计数值达到大于等于1。当计数值大于等于1,或者在待机中计数值大于等于1,对该计数进行减法并从dispatch_semaphore_wait函数返回。第二个参数与dispatch_group_wait相同。
var semaphore = dispatch_semaphore_create(1)
var semaphore_result = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 1 * Int64(NSEC_PER_SEC)))
if semaphore_result == 0 {
//由于计数大于等于1,所以计数减1.
//可执行需要进行排他控制的处理
}else {
//由于计数为0,因此在达到指定时间为止待机
}
dispatch_semaphore_wait函数返回0时,可安全地执行需要进行排他控制的处理。该处理结束时通过dispatch_semaphore_signal函数将计数加1。
//保证可访问array的线程同时只有一个
var array = [Int]()
for i in 0...100000 {
dispatch_async(globlDispatchQueueDefault, { () -> Void in
//等待Dispatch Semaphore,直到计数大于等于1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
//由于dispatch semaphore的计数大于等于1,所以计数减1,dispatch_semaphore_wait函数执行返回
//即执行到此
//dispatch semaphore的技术恒为0
//由于可访问array的线程只有1个,因此可安全的进行更新
array.append(i)
//通过排他控制处理
//所以通过dispatch_semaphore_signal函数
//将dispatch semaphore的计数加1
//如果有通过dispatch_semaphore_wait函数
//等待dispatch semaphore的计数值增加的线程
//就由最先等待的线程执行
dispatch_semaphore_signal(semaphore)
})
}
dispatch_once
dispatch_once函数是保证在应用程序执行中只执行一次指定处理的API。在多核CPU中,在正在更新表示是否初始化的标致变量时读取,就有可能多次执行初始化处理。这就是所说的单例模式,在生成单例对象时使用。
var initialized = false
if initialized == false {
//初始化
initialized = true
}
let pred:UnsafeMutablePointer<dispatch_once_t> = UnsafeMutablePointer(bitPattern: 0)
dispatch_once(pred) { () -> Void in
}
Dispatch I/O
在读取较大文件时,如果将文件分成合适的大小并使用Global Dispatch Queue并列读取的话,应该会比一般的读取速度快不少。Dispatch I/O和Dispatch Data。
通过Dispatch I/O读写文件时,使用Global Dispatch Queue将1个文件按某个大小read、write。
分割读取的数据通过使用Dispatch Data可更为简单地进行结合和分割。
OC代码:
var pipe_q = dispatch_queue_create("PipeQ", NULL)
var pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q) ^(int err){
close(fd);
});
*out_fd = fdpair[1];
dispatch_io_set_low_water(pipe_channel, SIZE_MAX);
dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q) ^(bool done, dispatch_data_t pipedata, int err ){
if (err == 0){
size_t len = dispatch_data_get_size(pipedata);
if (len > 0){
const char *bytes = NULL;
char *encode;
dispatch_data_t md = dispatch_data_create_map(pipedata, (const void **)bytes, &len);
encoded = asl_core_encode_buffer(bytes,len);
asl_set((aslmsg)merged_msg,ASL_KEY_AUX_DATA,encoded);
free(encoded);
_asl_send_message(NULL,merged_msg,-1,NULL);
asl_msg_release(merged_msg);
dispatch_release(md);
}
}
if (done) {
dispatch_semaphore_signal(sem);
dispatch_release(pipe_channel);
dispatch_release(pipe_q);
}
});