多线程基础

144
作者 IamOnelong
2016.04.19 22:50 字数 1285

线程、进程,线程安全,进程同步,可见性,一致性,锁,信号量,并发,并行

从操作系统概念上说,线程是最小的可执行单位,也就是系统调度的最小单位。进程是资源分配的最小单位。线程是依赖进程存在的,共享进程内的资源,如内存,cpu,io等。在操作系统的发展过程中,为了提高系统的稳定性、吞吐量和安全性,操作系统内核和用户态做了隔离,例如Linux有内核线程,用户线程,内核进程,用户进程,从根本上Linux是没有线程的,线程对Linux系统来说是个特殊的进程。那么用户线程和内核线程是一一对应呢?从宏观上看是一一对应的,在用户态的每一个线程,都会在内核有对应的执行线程,但是由于资源的限制,用户态的线程和内核线程是多对一的关系。用户进程和内核进程也类似。具体怎样对应的,这里就不探讨了。
为了提高操作系统的实时性,操作系统是以时间片轮转来实现任务调度的。理论上时间片内是不可以被中断的,可认为是cpu最小的单位执行时间。现代操作系统为了提高用户体验,线程都是抢占式的,而中断一般在时间片用完的时候发生。线程、进程和CPU都是多对一的关系,所以存在进程线程切换的问题。
线程内部还是有自己内存空间的,所以有个概念叫线程内存模型。线程内部有自己私有的本地内存,故线程和线程之间的本地内存存在可见性问题。例如全局变量A在线程1修改后,线程2并不一定能拿到A的修改值,因为线程1会把全局变量A拷贝到本地内存,修改后并不会马上同步。在编译的时候,编译器为了优化,(例如利用超线程技术)可能会重排指令的执行顺序,这就会存在一致性了。

线程安全

在线程安全里面经常要讨论的两个问题就是:可见性和一致性。锁是什么东西呢?锁就是一道内存屏障,保证可见性和一致性的一种策略,由操作系统甚至更底层的硬件提供。加锁是消耗资源的,特别是在多核CPU上,现在多核CPU一般有3级缓存,一级缓存通常是单核独占的,而线程的本地内存很可能就保存在cpu的缓存里面,然而加锁就意味着保证可见性和一致性,需要中断同步数据,保证别人拿到的是最新修改值。由于用途不同,锁被设计成各种各样的,如互斥锁,读写锁,自旋锁,同步块,数据库的事务等,如果只要保证可见性的,可以不使用锁,在java里面可以使用volatile修饰全局变量。虽然在c/c++,都有同样的修饰符,但是是不是一样的意思呢,请参考其他文章。

进程同步和进程通信

在操作系统里面,进程和进程之间是隔离的,所以线程上面的锁在进程同步上是没有作用的。进程通信都要依赖操作系统的机制完成,如信号量,文件锁,共享内存,管道,文件,socket等。进程间通信是很耗费资源的,都是系统调用。

并发和并行

从字面上说,并发是某段时间内执行,并行就是同时执行。对于单核CPU没有并行的,只有并发。在多核CPU编程里面线程安全显得特别重要。因为并行,非常容易出现数据一致性的问题,而锁和并行是一对矛盾体,锁是降低并行的东西。

iOS多线程

iOS是类unix系统发展过来的,当然苹果做了很多优化,使得在iOS上使用多线程变得简单,同时也保证稳定性和高效性。在iOS关于线程的api有pthread、NSThread,NSOperation,GCD。对于pthread就不说了,oc上基本没人用,除非做c/c++开发的人员。NSThread和runloop一起使用例子比较多,例如AFN 2.0的核心框架,YYImage等。NSOperation和GCD使用得最多,GCD最方便使用,但是不能很方便地控制并发数,不能很方便的取消任务。而NSOperation可以轻松做到。关于NSOperation,GCD的使用我就不多说了,感兴趣的可以看看SDWebImage、AFN 3.0等。具体入门基础的文章,我推荐如下:
http://www.molotang.com/articles/1149.html
http://www.molotang.com/articles/1737.html
http://www.cnblogs.com/ziyi--caolu/p/4846555.html
http://www.cnblogs.com/ziyi--caolu/p/4900650.html

iOS