什么是并行、并发
并行是指计算机利用多核技术,同时执行若干程序,每个cpu执行一个程序,这些程序同时执行。
并发是指一个cpu(也可以多个cpu)在若干程序之间切换执行任务,同一时刻只有一个程序的任务在执行。
详细内容可以参考这里
什么是串行、并行
串行就是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