本文参考《iOS与OS X多线程和内存管理》以及其他博客。
进程: 进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例�。程序运行时系统会创建一个进程,并为它分配资源,然后将该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。简单说,就是一个正在运行的应用程序。
线程: 线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量,线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。
一个进程至少有一个线程,即主线程。在iOS开发中,所有涉及UI界面的,必须在主线程中更新。
什么是GCD?
苹果官方给出解释:GCD是异步执行任务的技术之一,一般将应用程序中记述的线程管理代码在系统几种实现,开发者只需要定义想执行的任务更追加到适当的Dispatch Queue中,GCD就可以生成必要的线程并计划执行任务。
它有如下优点:
1. GCD可以将花费时间极长的任务放到后台线程,可以改善应用的相应性能。
2. GCD提供一个易于使用的并发模型而不仅仅只是锁和线程,以帮助我们避开并发陷阱。
3. GCD具有常见模式(如单例)上用更高性能的优化代码的潜在能力
GCD的一些术语
串行(Serial)与并发(Concurrent)
任务串行,在同一时间,有且只有一个任务被执行,即一个任务执行完毕之后再执行下一个任务。
任务并发,在同一时间,有多个任务被执行。
同步(Synchronous)与异步(Asynchronous)
同步,意味着在当前线程中执行任务,不具备开启新的线程的能力。
异步,在新的线程中执行任务,具备开启新的线程的能力。
在GCD中,这些术语描述当一个函数相对于另一个任务完成,此任务是该函数要求GCD执行的,一个同步函数只在完成了它预订的任务后才返回。一个异步函数,刚好相反,会立即返回,预订的任务会完成但不会等它完成,因此,一个异步函数不会阻塞当前线程去执行下一个函数。
临界区(Critical Section)
就是一段代码不能被并发执行,即两个线程不能同时执行这段代码。这很常见,因为代码去操作一个共享资源,如数据库写入操作。
死锁(Deadlock)
停止等待事情的线程会导致多个线程互相维持等待,即死锁。
两个或多个线程都卡主了,并等待对方完成或执行其他操作。如在串行队列中使用该串行队列同步的执行任务。
线程安全(Thread Safe)
线程安全的代码能在多线程或并发任务中被安全的调用,而不会导致问题(数据损坏,崩溃等)。线程不安全的代码在某个时刻只能在一个上下文中运行。线程安全的代码如NSDictionary,可以在同一时间在多个线程中使用它而不会有问题。另线程不安全代码如NSMutableDictionary,应该保证一次只能有一个线程访问它。
上下文切换(Context Switch)
一个上下文切换指当你在单个进程里切换执行不同的线程时存储与恢复执行状态的过程。
并发和并行
并行要求并发,但并发不能保证并行。开启线程是很耗性能的,事实上,在某次并行处理任务中,开启的线程数量是有限的,如果上限为2,每次开启的新线程是2,那么有可能出现并发却并不并行的情况。
并发代码的不同部分可以“同步”执行,但具体情况都依赖系统,多核设备通过并行来同时执行多个线程,而单核设备要实现这一点,必须先运行一个线程,执行一个上下文切换,然后运行另一个线程或进程。这通常发生的足够快以至我们会有并发执行的错觉。
队列(Queue)
苹果官方说明:开发者要做的只是定义想执行的任务并追加到适当的Dispatch Queue中。
dispatch_async(queue,^{
/**
* 想要执行的任务
*/
});
该源码使用Block“定义想执行的任务”,通过dispatch_async函数“追加”赋值在变量queue的Dispatch Queue中。这样,可以使得指定的Block在另一线程中执行。
GCD提供Dispatch queue 来出来代码块,这些对联管理开发者提供给GCD的任务并用FIFO(先进先出)顺序执行这些任务。这就保证了第一个被添加到队列里的任务会是队列中第一个开始的任务,而第二个被添加的任务将第二个开始,如此直到队列的终点。
串行队列(Serial Dispatch Queue)
这些任务执行时受到GCD的控制,唯一能确保的事情是GCD一次只执行一个任务,并且按照我们添加到队列的顺序来执行。
dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.test", DISPATCH_QUEUE_SERIAL);
dispatch_async(dispatchQueue, ^{
NSLog(@"任务一开始");
sleep(5);
NSLog(@"任务一完成");
});
dispatch_async(dispatchQueue, ^{
NSLog(@"任务二开始");
sleep(4);
NSLog(@"任务二完成");
});
dispatch_async(dispatchQueue, ^{
NSLog(@"任务三开始");
sleep(3);
NSLog(@"任务三完成");
});
打印结果如下并发队列(Concurrent Dispatch Queue) 并发队列中的任务能得到的保证是它们会按照被添加的顺序开始执行。任务是以任意顺序完成,你不会知道何时开始下一个任务,或者任意时刻有多少Block执行,这完全取决于GCD
dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.test", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 100; i++) {
dispatch_async(dispatchQueue, ^{
NSLog(@"%d %@",i, [NSThread currentThread]);
});
}
打印结果可以看到,不是顺序执行任务,开了多个线程。使用多个线程同时处理多个任务,因为完成任务所需要消耗的时间不同,完成任务的最终时间不同。