首先介绍几个概念
线程:
1. 线程是进程的基本执行单元,一个进程的所有任务都是在线程中执行的。
2. 一个线程中的任务执行是串行的。
3. 每个进程都有至少一条线程,默认的线程为主线程。
多线程:
1. 原理:同一时间,CPU 只能处理一条线程,意味着只有一条线程在工作。因为 CPU 的处理速度非常高,所以会产生很多空闲时间(时间碎皮)。多线程并发的原理,其实就是 CPU 快速的在多条线程之间调度。
2. 优点:提高程序执行效率。提高 CPU,内存等资源的利用效率。
3. 缺点:开启线程需要占用内存空间(一般主线程占1M,子线程占512K),如果开启大量子线程,会占用大量内存空间,降低系统性能。线程太多,CPU 调度就会非常频繁,加大 CPU 功耗。多线程也会使程序设计更加复杂,因为需要考虑到线程之间的通信和数据共享。
iOS 的开发与多线程:
一个 iOS 程序运行之后,会创建一条线程,称为“主线程”或者“UI线程”
主线程主要用于处理UI事件 显示,点击,滚动,拖拽 等等
耗时操作会卡主主线程,严重影响UI 的流畅度。
iOS 常用线程模型有如下四种
1. pthread - 纯 C 实现,使用率最低。作为了解即可,日常冷门装逼用。
2. NSThread - OC 线程库,实用率不高,仅作简单调用,比如获取当前线程什么的。
3. NSOperationQueue - 线程池/线程队列,实用率高,方便简洁高效。
4. GCD - Block 模式的线程池,C 实现,项目中也被广泛实用。
下面开实例 ================================
1. phread 简单的创建
需要引入头文件
#import <pthread.h>
pthread_t thread;
pthread_create(&thread, NULL, run, NULL);
第一个参数是声明的线程变量地址,创建的线程会调用第三个函数指针指向的函数。
void * run() {
NSLog(@"奔跑, 线程: %@", [NSThread currentThread]);
return NULL;
}
函数中使用了 NSThread 的方法获取了当前的线程并打印出来。
运行结果:
2015-08-28 20:43:31.230 ThreadTest[1773:117469] 奔跑, 线程: <NSThread: 0x7f9f83597c50>{number = 2, name = (null)}
通过结果可以看出,线程的 number 为 2 ,这意味着我们的函数并没有执行在 number 为 0 的主线程上,而是新开辟了一个线程。
2. NSThread
用一个简单的例子来介绍NSThread 的几个常用的属性和方法。
- (void)viewDidLoad {
[super viewDidLoad];
NSThread * thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
NSLog(@"这个输出证明虽然创建了 thread , 但是没有调用 start 就没办法自动执行。");
//如果需要让线程执行任务,需要调用 start 方法
[thread start];
}
- (void)run {
//[NSThread currentThread] 获取当前线程
NSLog(@"%@", [NSThread currentThread]);
//[NSThread isMultiThreaded] 判断是否为多线程
NSLog(@"%d", [NSThread isMultiThreaded]);
//[NSThread isMainThread] 判断当前线程是否为主线程
NSLog(@"%d", [NSThread isMainThread]);
}
输出结果如下:
2015-08-28 21:19:23.369 ThreadTest[1864:128703] 这个输出证明虽然创建了 thread , 但是没有调用 start 就没办法自动执行。
2015-08-28 21:19:23.390 ThreadTest[1864:128839] <NSThread: 0x7fb98a496f20>{number = 2, name = (null)}
2015-08-28 21:19:23.391 ThreadTest[1864:128839] 1
2015-08-28 21:19:23.391 ThreadTest[1864:128839] 0
从结果可以得知,在 start 方法被执行后,加入线程的任务被执行,即函数被调用,当前线程是多线程,且不是主线程。
3. GCD
GCD 弥补了 NSThread 难于管理的问题。
GCD 采用队列的方式管理线程
通过 GCD,开发者可以不用和线程打交道,而是直接向队列中扔代码Block 块就可以。
通过一个实例简单实现:
//创建一个 GCD 队列
dispatch_queue_t myQueue = dispatch_queue_create("myQueue", NULL);
dispatch_queue_t myQueue2 = dispatch_queue_create("myQueue2", NULL);
dispatch_async(myQueue, ^{
NSLog(@"myQueue - currentThread:%@, ",[NSThread currentThread]);
});
dispatch_async(myQueue2, ^{
NSLog(@"myQueue2 - currentThread:%@, ",[NSThread currentThread]);
});
执行打印结果如下:
2015-08-28 21:37:42.086 ThreadTest[1934:135106] myQueue - currentThread:<NSThread: 0x7fac095bffa0>{number = 2, name = (null)},
2015-08-28 21:37:42.086 ThreadTest[1934:135105] myQueue2 - currentThread:<NSThread: 0x7fac09484980>{number = 3, name = (null)},
想要执行 GCD 的 dispatch_async 方法,必须把它交给一个队列管理,否则会报错。
dispatch_async 函数会把传入的 block 块放入指定的 queue 里面运行。这个函数是异步的,这就意味着它会立即返回而不管 block 块是否运行结束。
也可以通过获取全局变量的方式:
//获取全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(queue, ^{
NSLog(@"queue1:%@",[NSThread currentThread]);
});
//直接用
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"queue2:%@",[NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"queue3:%@",[NSThread currentThread]);
});
其中赋值的参数是优先级
运行结果:
2015-08-28 21:46:00.726 ThreadTest[1995:138596] queue2:<NSThread: 0x7fd20158f420>{number = 2, name = (null)}
2015-08-28 21:46:00.728 ThreadTest[1995:138597] queue3:<NSThread: 0x7fd2014ceb20>{number = 3, name = (null)}
2015-08-28 21:46:00.729 ThreadTest[1995:138598] queue1:<NSThread: 0x7fd201583f10>{number = 4, name = (null)}
4. NSOperationQueue
NSOperationQueue的底层就是基于 GCD 实现的,高度分装性使其具备更加易用的好处。
实例:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSInvocationOperation *iop = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
NSBlockOperation *bop = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Block 任务 %@",[NSThread currentThread]);
}];
[bop addExecutionBlock:^{
NSLog(@"新添加的任务 线程 : %@", [NSThread currentThread]);
}];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//把任务交给队列来执行和管理
[queue addOperation:iop];
[queue addOperation:bop];
}
- (void)run {
NSLog(@"Invocation 任务 线程:%@", [NSThread currentThread]);
}
block方式 可以后续追加任务到 block 中。
执行结果如下:
2015-08-28 21:55:13.282 ThreadTest[2046:142195] Invocation 任务 线程:<NSThread: 0x7ff9e0cdd0b0>{number = 2, name = (null)}
2015-08-28 21:55:13.282 ThreadTest[2046:142194] Block 任务 <NSThread: 0x7ff9e0d7b320>{number = 3, name = (null)}
2015-08-28 21:55:13.282 ThreadTest[2046:142193] 新添加的任务 线程 : <NSThread: 0x7ff9e0d78910>{number = 4, name = (null)}