现在iOS的多线程方案主要有以下这几种:
1. GCD(Grand Central Dispatch):使用dispatch queue(分派队列)执行tasks(任务),苹果公司推荐使用;
2. NSOperationQueue和NSOperation:使用operation queue(操作队列)执行operations(操作),苹果公司推荐使用;
3. NSThread:苹果公司封装的基于OC对象的线程对象,但是需要自己进行线程生命周期的控制,以及对共享资源的同步操作。与前两种方式相比,不推荐使用这种方式。
4. Pthreads:POSIX thread(Portable Operating System Interface of UNIX【可移植操作系统接口】线程),是线程的POSIX标准,定义了创建和操纵线程的一套API。iOS和Mac OS中的线程的底层实现就是基于它的,是很底层的API,除非要自己从底层开始实现一套多线程方案,否则一般不会用这个。
一、GCD(Grand Central Dispatch)
在讨论GCD用法之前,我们需要先对GCD涉及的几个概念有些了解:
1. 任务(tasks):就是需要做的事情,也就是要用多线程执行的一段代码。在GCD中提交给分派队列的任务必须被包含在一个函数或者block对象中,随后调用dispatch_[a]sync()方法或者dispatch_[a]sync_f()方法添加到对应分派队列中。
Block objects are a C language feature introduced in OS X v10.6 and iOS 4.0 that are similar to function pointers conceptually, but have some additional benefits. Instead of defining blocks in their own lexical scope, you typically define blocks inside another function or method so that they can access other variables from that function or method. Blocks can also be moved out of their original scope and copied onto the heap, which is what happens when you submit them to a dispatch queue. – 加入到Dispatch queue的block会被复制到堆中。
2. 分派队列(dispatch queue):任务是通过分派到分派队列中后,系统才会自动管理一些线程执行对应任务的。GCD中队列分为两种:串行队列(Serial queues)和并行队列(Concurrent queues)。
串行队列:队列的先进先出(FIFO)原则,串行队列会根据任务被加入到队列的顺序,依次取出任务执行,一个执行完才能开始下一个。当前正在运行的任务是在由dispatch queue管理的特定的线程执行(但是这个任务的线程跟下个任务的线程不一定一样)。串行队列通常用于对特定资源的同步访问(类似于“锁”机制,防止共享资源的同时访问)。你可以创建任意个串行队列,对于各个串行队列之间来说,就相当于他们中正在执行的任务是在并行执行的!
使用
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
方法创建dispatch queue。
第一个参数传递队列名,如,“com.example.myqueue”;
第二个参数attr传递创建队列的类型,In macOS 10.7 and later or iOS 4.3 and later, specify DISPATCH_QUEUE_SERIAL (or NULL) to create a serial queue or specify DISPATCH_QUEUE_CONCURRENT to create a concurrent queue. In earlier versions, you must specify NULL for this parameter.(一般会创建串行队列吧,并行队列通常会通过dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
获取)dispatch_queue_create()方法创建的队列,在非ARC内存管理模式下,要注意对queue的内存管理。
并行队列:队列的先进先出(FIFO)原则,串行队列会并行地执行一个或多个任务,但是这些任务取出来开始执行的顺序还是依照的它们加入到队列中的顺序(后一个加入的并不用等到前一个执行完才能从队列中取出开始执行,而是依次都取出,然后放入到另一个新的线程中也执行,所以只是差一个取出的时间,很短,几乎可以忽略不计,所以可以看做是这些任务在同时执行)。这些任务所在的线程也都是由dispatch queue管理的,系统会根据情况处理当前应该同时开几个线程用于执行这些并发任务。
任务是要执行的代码;队列是用于保存以及管理任务的;线程是代码执行的通道,负责从队列中取任务执行。
然后放入队列中的任务的执行方式有两种,同步执行和异步执行。
同步执行方式对应调用GCD中的dispatch_sync()或dispatch_sync_f()方法;
异步执行方式对应调用GCD中的dispatch_async()或dispatch_async_f()方法;
调用同步方法执行任务时,无论任务时加在什么队列中,都需要按顺序先执行这些任务,前一个任务执行完成才能执行下一个任务,当前正在执行的代码也需要等待这些任务执行完成才能继续执行后面的代码!(用的都是调用GCD代码的同一个线程,一次只能执行一个任务,所以当前代码等待,任务一个一个顺序执行,全部执行完成后,当前代码继续往后执行!)但是注意,在串行队列中同时执行任务时,这些任务不能加入到当前同样的串行队列中,比如不能在主线程中不能将一个任务添加到main queue,又调用dispatch_sync()方法执行(因为串行要队列要当前的任务完了才能下一个,但是同步又要求停下当前任务,等同步任务执行完毕,这会造成队列的死锁)。官方有下面的说法:
Important: You should never call the
dispatch_sync
ordispatch_sync_f
function from a task that is executing in the same queue that you are planning to pass to the function. This is particularly important for serial queues, which are guaranteed to deadlock, but should also be avoided for concurrent queues.(官方是说串行队列这么做一定会deadlock,并行队列也最好不要这么做!)