GCD总结

什么是并行、并发

并行是指计算机利用多核技术,同时执行若干程序,每个cpu执行一个程序,这些程序同时执行。

GCD总结_第1张图片
并行图

并发是指一个cpu(也可以多个cpu)在若干程序之间切换执行任务,同一时刻只有一个程序的任务在执行。

GCD总结_第2张图片
并发图

详细内容可以参考这里


什么是串行、并行

串行就是FIFO,first in first out,翻译成中文的意思就是先进先出,纵队。
并行就是多个事件同时发生,横队。

什么是同步、异步

同步是指调用一个函数去执行某件事情,当前线程会一直等待该函数的返回结果。只有函数返回了,才会往下继续执行,这样一件事情一件事情的执行。
异步则是调用一个函数去执行某件事情,不用等待该函数的返回值,继续往下执行。

什么是线程、进程

进程(process)是指程序在运行的时候,计算机为这个程序分配的资源集合,每个程序只有一个进程,计算机会为每个进程分配一个PID,来区分不同的应用程序进程。
线程(thread)是更小的资源集合,一个进程可以创建多个线程,利用这些线程来完成应用程序需要处理的各种事件和任务。

什么是主线程、子线程

主线程是指项目启动时一直存在的,用来处理触摸滑动按压等事件,响应用户交互的线程。子线程是指其他不用处理UI事件,辅助完成程序任务的线程。主线程占用1K的资源,子线程占用512KB。主线程的性能是子线程的一倍。

在iOS 开发的过程中,可以操作线程的API主要有四个
pthread (c语言)
NSThread
GCD (最高效)
NSOperation(基于GCD)

实际开发中运用的比较多的是GCD和NSOperation 这两种API来做多线程的编程。

先主要来讲GCD的使用

创建串行队列

dispatch_queue_t * serialQueue = dispatch_queue_create("com.zhouzewen.threadID", NULL); dispatch_queue_t * mainQueue = dispatch_get_main_queue();
NULL 参数也可以使用宏DISPATCH_QUEUE_SERIAL 两者是相同的意思。mainQueue是获得在主线程中执行的dispatch queue。追加到main dispatch queue的处理会在主线程的Runloop中执行。serialQueue,会一个一个的执行添加到队列中的任务,不管多少个。

创建并行队列

dispatch_queue_t * concurrentQueue = dispatch_queue_create("com.zhouzewen.threadID", DISPATCH_QUEUE_CONCURRENT); dispatch_queue_t * concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
两种方式获得并行队列,一种是自己通过函数创建,另外一种是所有应用程序都能够使用的concurrent dispatch queue,只需要制定指定自己需要的优先级就可以了。concurrentQueue会把添加到队列中的任务同时执行。

名称 Dispatch Queue 种类 说明
Main Dispatch Queue Serial Dispatch Queue 主线程执行
Global Dispatch Queue(High Priority) Concurrent Queue 最高先级
Global Dispatch Queue(Default Priority) Concurrent Queue 默认优先级
Global Dispatch Queue(Low Priority) Concurrent Queue 低优先级
Global Dispatch Queue(Background Priority) Concurrent Queue 后台执行
派发队列的两种方式

1.同步派发
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
2.异步派发
dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
两者的区别是 sync 同步派发,当前线程会等待 queue 执行完block中的任务,然后返回void,async 异步派发,dispatch_async函数会立刻返回void,不会阻塞当前线程,queue 异步的执行block当中的任务。

修改队列的优先级

dispatch_queue_create函数生成的 dispatch_queue不管是 serialQueue还是concurrentQueue,都和global dispatch queue 的default优先级相同。如果需要改变dispatch_queue_create 函数生成的优先级那么需要使用dispatch_set_target_queue函数。
dispatch_set_target_queue(<#dispatch_object_t _Nonnull object#>, <#dispatch_queue_t _Nullable queue#>)
需要注意的一点是不能修改系统dispatch queue的优先级,并行队列修改和串行队列优先级相同之后会变成串行队列。

任务延迟执行

若有需要将一个block 延迟一段时间再操作的需求,就需要用到dispatch_after
dispatch_after(<#dispatch_time_t when#>, <#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
需要注意的是,block中的内容不会再time之后立刻执行,而是需要在time之后被添加到queue中,根据queue的Runloop的时间来确定。以主线线程为例,Runloop六十分之一秒循环一次。所以block最慢会在time+1/60执行,当然如果线程中有非常耗时的操作,那么block必须等待耗时操作完成之后才会执行。

任务最后执行

对于串行队列而言非常的简单就可以实现这个需求,只需要把最后要执行的block添加到串行队列最后。
但是对于并行队列而言,因为每个任务执行的时间不确定所以,所以非常要在所有的任务执行完毕之后再来执行block非常的麻烦。dispatch_group可以比较简单的完成这种需求。
dispatch_group_create()创建dispatch_group
dispatch_group_async(<#dispatch_group_t _Nonnull group#>, <#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
添加队列到group中,最后dispatch_group_notify(<#dispatch_group_t _Nonnull group#>, <#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
这个block会最后执行,而且当前线程会一直被dispatch_group_notify函数阻塞。
dispatch_group_wait 也可以达到上面的效果。

处理数据竞争

在访问数据库的时候经常会遇到这样的问题,读操作和写操作可能同时操作到一个数据,这样就会带来数据竞争的问题。希望的效果是所有的读操作都可以并行,这样效率高。但是写操作是具有排他性的,要用串行队列执行。
dispatch_barrier_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)queue可能执行了很多操作,dispatch_barrier_async函数会等待queue中其他所有之前添加的block执行完毕之后,再单独执行这个函数添加的block,然后变成正常的并行队列。所以可以把读操作写到dispatch_barrier_async的block中。

一次添加多个任务

dispatch_apply(<#size_t iterations#>, <#dispatch_queue_t _Nonnull queue#>, <#^(size_t)block#>)可以一次性添加多个block到一个队列中,并等待所有这些block执行完毕。

暂停或恢复任务

dispatch_suspend(<#dispatch_object_t _Nonnull object#>)暂停队列,但是对已经提交到队列中的block的执行时没有影响的。
dispatch_resume(<#dispatch_object_t _Nonnull object#>)则可以将暂停的队列恢复,让队列可以继续执行任务。

信号量

可以进行更细的排他控制,比如读操作每次都只能有一个线程来操作。
dispatch_semaphore

单例

dispatch_once 保证该函数只会在应用程序声明周期内执行一次。

读写

在读取较大的文件的时候,将其分割成合适的大小,使用global dispatch queue并列读取可以提高读取的速度。

多线程编程需要面对的三大问题,数据竞争,死锁,线程数过多。

线程间的通信

参考资料

进程和线程的比喻
进程和线程的区别
IO
http://blog.csdn.net/z_5502110036/article/details/70336309
http://www.jianshu.com/p/da4710d76d33
block阅读资料
apple实现关于扩充C语音的概要文档,研究了block的实现,并参考已经存在于ARC基本理论中的__strong __weak 两个修饰符的实现
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1370.pdf
apple 向C99追加 blocks的提案书
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1451.pdf
apple 介绍blocks的资料
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1457.pdf
LLVM中Blocks语言规范文档,和下面的文档一起理解,可实现Blocks的编译器和运行时库,想编译器支持Blocks,必读文档。
http://clang.llvm.org/docs/BlockLanguageSpec.html
LLVM中Blocks实现规范文档,存在支持Blocks的编译器而没有运行时库时,阅读理解该文档可以解决问题
http://clang.llvm.org/docs/Block-ABI-Apple.html
libclosure 为apple提供的用于Blocks的运行时库,该库提供了Block_copy函数和Block_release函数 API
https://opensource.apple.com/source/libclosure/libclosure-53/
这个运行时库可以实现不支持Blocks的操作系统(如iOS3.0)中,也能使用Blocks。
旧操作系统的运行时库不包含Block_copy函数和Block_release函数
使用plblocks独立实现的库,可以在旧操作系统上实现Blocks
https://code.google.com/archive/p/plblocks/

GCD阅读资料
Libdispatch 提供GCD的API,apple的库,实现了dispatch queue
https://opensource.apple.com/source/libdispatch/libdispatch-187.5/

实现了GCD中使用的pthread_workqueue API
https://opensource.apple.com/source/Libc/Libc-763.11/
pthreads/pthread.c
pthreads/pthread_workqueue.h

Mac OS X 和 iOS 的核心 XNU内核源代码
https://opensource.apple.com/source/xnu/xnu-1699.22.81/
GCD使用XNU内核的workqueue实现
bsd/kern/pthread_synch.c
osfmk/kern/thread.c

apple 提供的libdispatch开源页面,可查看libdispatch邮件列表和向其他操作系统移植libdispatch的情况
https://libdispatch.macosforge.org

你可能感兴趣的:(GCD总结)