1基本概念
线程(线程)用于指代独立执行的代码段。
进程(process)用于指代一个正在运行的可执行程序,它可以包含多个线程。
任务(task)用于指代抽象的概念,表示需要执行工作。
多线程让应用程序内拥有多个可执行路径,会给你的代码增加更多的复杂性。每个 线程需要和其他线程协调其行为,以防止它破坏应用程序的状态信息。因为应用程序 内的多个线程共享内存空间,它们访问相同的数据结构。如果两个线程试图同时处理 相同的数据结构,一个线程有可能覆盖另外线程的改动导致破坏该数据结构。即使有 适当的保护,你仍然要注意由于编译器的优化导致给你代码产生的微妙影响。
线程启动之后,线 程就进入三个状态中的任何一个:运行(running)、就绪(ready)、阻塞(blocked)。如 果一个线程当前没有运行,那么它不是处于阻塞,就是等待外部输入,或者已经准备 就绪等待分配 CPU。线程持续在这三个状态之间切换,直到它最终退出或者进入中断 状态。
当你显式的中断线程的时候,线程永久停止,且被系统回收。因为线程创建需 要的内存和时间消耗都比较大,因此建议你的入口点函数做相当数量的工作,或建立 一个运行循环允许进行经常性的工作。
Run Loops (运行 循环 )
为了配置 run loop,你所需要做的是启动你的线程,获取 run loop 的对象引用, 设置你的事件处理程序,并告诉 run loop 运行。Cocoa 和 Carbon 提供的基础设施会 自动为你的主线程配置相应的 run loop。如果你打算创建长时间运行的辅助线程, 那么你必须为你的线程配置相应的 run loop。
同步工具
线程编程的危害之一是在多个线程之间的资源争夺。如果多个线程在同一个时间 试图使用或者修改同一个资源,就会出现问题。缓解该问题的方法之一是消除共享资 源,并确保每个线程都有在它操作的资源上面的独特设置。因为保持完全独立的资源 是不可行的,所以你可能必须使用锁,条件,原子操作和其他技术来同步资源的访问
锁
锁提供了一次只有一个线程可以执行代码的有效保护形式。最普遍的一种锁是互 斥排他锁,也就是我们通常所说的“mutex”。
除了锁,系统还提供了条件,确保在你的应用程序任务执行的适当顺序。一个条 件作为一个看门人,阻塞给定的线程,直到它代表的条件变为真。当发生这种情况的 时候,条件释放该线程并允许它继续执行
尽管锁和条件在并发设计中使用非常普遍,原子操作也是另外一种保护和同步访 问数据的方法。原子操作在以下情况的时候提供了替代锁的轻量级的方法,其中你可 以执行标量数据类型的数学或逻辑运算
线程间通信
虽然一个良好的设计最大限度地减少所需的通信量,但在某些时候,线程之间的 通信显得十分必要。
线程可能需要处理新的工作要求,或向你应用程 序的主线程报告其进度情况。在这些情况下,你需要一个方式来从其他线程获取信息。 幸运的是,线程共享相同的进程空间,意味着你可以有大量的可选项来进行通信,“配置线程局部存储”。
2设计技巧
避免显式创建线程
比如 GCD 和操作对象技术被设计用来管 理线程,比通过自己的代码根据当前的负载调整活动线程的数量更高效
避免共享数据结构
避免造成线程相关资源冲突的最简单最容易的办法是给你应用程序的每个线程一份它需求的数据的副本。
主线程刷新你的UI界面
主线程里面接收和界面相关的事件和初始化更新你的界面
3线程管理(考虑线程成本)
创建一个线程
使用NSThread
设置线程的优先级
4Run Loops
5线程同步
同步工具:1原子操作 2锁 3条件
线程安全设计的技巧:
1完全避免同步:实现并发最好的方法是减少你并发任务之间的交互和相互依赖。如果每个任务在它自己的数据集上面操作,那它不需要使用锁来保护这些数据。甚至如果两个任务共享一个普通数据集,你可以查看分区方法,它们设置或提供拷贝每一项任务的方法。当然,拷贝数据集本身也需要成本,所以在你做出决定前,你需要权衡这些成本和使用同步工具造成的成本那个更可以接受。
当心死锁(Deadlocks)和活锁(Livelocks):
任何时候线程试图同时获得多于一个锁,都有可能引发潜在的死锁。当两个不同的线程分别保持一个锁(而该锁是另外一个线程需要的)又试图获得另外线程保持的锁时就会发生死锁。结果是每个线程都会进入持久性阻塞状态,因为它永远不可能获得另外那个锁。
一个活锁和死锁类似,当两个线程竞争同一个资源的时候就可能发生活锁。在发生活锁的情况里,一个线程放弃它的第一个锁并试图获得第二个锁。一旦它获得第二个锁,它返回并试图再次获得一个锁。线程就会被锁起来,因为它花费所有的时间来释放一个锁,并试图获取其他锁,而不做实际的工作。
避免死锁和活锁的最好方法是同一个时间只拥有一个锁。如果你必须在同一时间获取多于一个锁,你应该确保其他线程没有做类似的事情。
使用原子操作:
获取一个锁是一个很昂贵的操作,即使在无竞争的状态下。相比,许多原子操作花费很少的时间来完成操作也可以达到和锁一样的效果。
使用锁:
1使用POSIX互斥锁:为了新建一个互斥锁,你声明并初始化一个pthread_mutex_t的结构。为了锁住和解锁一个互斥锁,你可以使用pthread_mutex_lock和pthread_mutex_unlock函数。
2使用NSLock类:
3使用@synchronized指令
4使用NSRecursiveLock对象
5使用NSConditionLock对象
6使用NSDistributedLock对象
使用条件
1使用NSCondition类
2使用POSIX条件