概述
多线程,是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个执行绪,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在iOS中每个进程启动后都会建立一个主线程,这个线程是其他线程的父线程。iOS中多线程使用并不复杂,关键是如何控制好各个线程的执行顺序、处理好资源竞争问题。在iOS开发中,常用的多线程开发方式有以下四种方式:
- pthread
- NSThread
- GCD
- NSOperation & NSOperationQueue
pthread
POSIX线程(POSIX Threads,常被缩写为Pthreads)是POSIX的线程标准,定义了创建和操纵线程的一套API。实现POSIX线程标准的库常被称作Pthreads,一般用于类Unix系统,如Linux、OSX。Pthreads定义了一套C语言的类型、函数与常量,它以pthread.h头文件和一个线程库实现。在iOS中使用的不是很多,因此在这里只是简单的介绍下。
- 在iOS中使用pthread(线程的创建以及参数的传递)。
1 | static void* task(void *args) |
pthread是一套基于c语言的框架,在iOS中使用pthread需要使用c语言,而且在参数传递的时候如果传递的是堆内存数据需要自己管理内存,因此在使用的时候没有iOS中的NSThread、GCD等线程方便,不过它也有其它的优点,比如在类Unix系统上很容易移植。
NSThread
NSThread是轻量级的多线程开发,提供的接口相对比较少,使用起来也并不复杂。值得注意的是使用NSThread需要自己管理线程生命周期。由于NSThread在处理线程并发、线程同步的问题上相对于其它的高级API并没有特别的优势。因此,在开发的时候使用的也相对较少。
- 创建并自动启动。
1 | - (void)createThread |
- 创建一个线程对象,然后调用start方法启动线程。
1 | - (void)createThread |
- NSObject创建线程,并启动。
1 | - (void)createThread |
GCD
GCD(Grand Central Dispatch)是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在OSX10.6雪豹中首次推出,也可在iOS4及以上版本使用。之所以使用GCD,是因为它的诸多好处:1、GCD可用于多核的并行运算;2、GCD会自动利用更多的CPU内核;3、GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)。
GCD任务与队列
GCD任务:就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在GCD中是放在block中的。执行任务有两种方式:同步执行和异步执行。两者的主要区别是:是否具备开启新线程的能力。
同步执行(sync):在当前线程中运行,不开启新线程
异步执行(async):开启新线程,任务在新线程中执行队列:这里的队列指任务队列,即用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在GCD中有两种队列:串行队列和并行队列。
并行队列(Concurrent Dispatch Queue):自动开启多个线程让多个任务同时执行。
串行队列(Serial Dispatch Queue):多个任务一个个有序地执行。GCD队列的创建
并行队列
1
2
3
4
5
6
7
8
9
10- (void)createConcurrentQueue
{
dispatch_queue_t queue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"name1 = %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"name2 = %@", [NSThread currentThread]);
});
}
**串行队列**
1
2
3
4
5
6
7
8
9
10
- (void)createSerialQueue
{
dispatch_queue_t queue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"name1 = %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"name2 = %@", [NSThread currentThread]);
});
}
**内置队列**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)internalQueue
{
// MainQueue
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
NSLog(@"name = %@", [NSThread currentThread]);
});
// GloableQueue
dispatch_queue_t gloableQueue = dispatch_get_global_queue(0, 0);
dispatch_async(gloableQueue, ^{
NSLog(@"name = %@", [NSThread currentThread]);
});
}
GCD队列的执行
同步执行
1
2
3dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"name = %@", [NSThread currentThread]);
});
**异步执行**
1
2
3
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"name = %@", [NSThread currentThread]);
});
**延时执行**
1
2
3
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"run");
});
**一次性执行**
1
2
3
4
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"run");
});
- GCD栅栏创建
在我们需要异步执行多个操作的时候,并且这些操作具有一定的先后顺序,我们就可以使用栅栏来分割多个任务。比如:任务1、2执行完成后才能执行任务3、4,则我们可以使用栅栏来分割任务。
1 | - (void)barrierQueue |
- GCD的队列组
我们有时候有这样的需求,当多个任务执行的时候,我们想要在这些任务都执行完后去做其它的任务。这时候我们可以使用队列组,队列组可以将很多队列添加到一个组里,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。
1 | dispatch_group_t group = dispatch_group_create(); |
NSOperation & NSOperationQueue
NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用、代码可读性也更高。NSOperation需要配合NSOperationQueue来实现多线程。因为默认情况下,NSOperation单独使用时系统同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行。
- 创建任务
NSOperation是个抽象类,并不能封装任务。我们可以使用它的相关子类NSInvocationOperation、NSBlockOperation来创建任务。
**NSInvocationOperation**
1
2
3
4
5
6
7
8
9
10
- (void)InvocationOperation
{
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
[op start];
}
- (void)run
{
NSLog(@"------%@", [NSThread currentThread]);
}
**NSBlockOperation**
NSBlockOperation除了基本用法外,还提供了一个方法addExecutionBlock,通过addExecutionBlock就可以为NSBlockOperation添加额外的操作,这些额外的操作就会在其他线程并发执行。
1
2
3
4
5
6
7
8
- (void)blockOperation
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---name = ---%@", [NSThread currentThread]);
}];
[op start];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (void)blockOperation
{
// 主线程执行
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1-%@", [NSThread currentThread]);
}];
// 在子线程执行
[op addExecutionBlock:^{
NSLog(@"2-%@", [NSThread currentThread]);
}];
// 在子线程执行
[op addExecutionBlock:^{
NSLog(@"3-%@", [NSThread currentThread]);
}];
// 在子线程执行
[op addExecutionBlock:^{
NSLog(@"4-%@", [NSThread currentThread]);
}];
[op start];
}
**自定义NSOperation**
新建MyOperation继承自NSOperation的子类,重写main方法。如果单独使用MyOperation的情况下,是在主线程执行操作,并没有开启新线程。如果需要开启新线程,则需要配合NSOperationQueue的使用。
1
2
3
4
5
#import <Foundation/Foundation.h>
@interface MyOperation : NSOperation
@end
1
2
3
4
5
6
7
8
9
10
#import "MyOperation.h"
@implementation MyOperation
- (void)main
{
NSLog(@"name = %@",[NSThread currentThread]);
}
@end
内置队列
主队列
1
NSOperationQueue *queue = [NSOperationQueue mainQueue];
队列的执行
串行
1
2
3
4
5
6
7
8
9
10// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 设置最大并发操作数,为1为串行队列
queue.maxConcurrentOperationCount = 1;
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"name = %@", [NSThread currentThread]);
}];
**并行**
1
2
3
4
5
6
7
8
9
10
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 设置最大并发操作数,大于1为串行队列
queue.maxConcurrentOperationCount = 10;
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"name = %@", [NSThread currentThread]);
}];
队列的依赖
NSOperation和NSOperationQueue可以添加依赖关系。如果有两个任务1、2,需要1执行完之后,才能执行任务2,则可以让任务2添加依赖任务1。1
2
3
4
5
6
7
8
9
10
11
12
13
14NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"name1 = %@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"name2 = %@", [NSThread currentThread]);
}];
// op2依赖于op1
[op2 addDependency:op1];
[queue addOperation:op1];
[queue addOperation:op2];队列取消
1
2- (void)cancel;
- (void)cancelAllOperations;队列暂停
1 | - (void)setSuspended:(BOOL)suspended; |