目录html
进程是指在系统中正在运行的一个应用程序。每一个进程之间是独立的,每一个进程均运行在其专用且受保护的内存空间内。ios
基本概念:一个进程要想执行任务,必须得有线程(每个进程至少要有一条线程),线程是进程的基本执行单元,一个进程(程序)的全部任务都在线程中执行。
线程的串行:一条线程中任务的执行是串行的,若是要在一条线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。也就是说,在同一时间内,一条线程只能执行一个任务。编程
基本概念:即一个进程中能够开启多个线程,每条线程能够并行(同时)执行不一样的任务。
线程的并行:并行即同时执行。好比同时开启3条线程分别下载3个文件(分别是文件A、文件B、文件C)。
多线程并发执行的原理:在同一时间里,CPU只能处理1条线程,只有1条线程在工做(执行)。多线程并发(同时)执行,实际上是CPU快速地在多条线程之间调度(切换),若是CPU调度线程的时间足够快,就形成了多线程并发执行的假象。
多线程优缺点
优势:能适当提升程序的执行效率、能适当提升资源利用率(CPU、内存利用率)
缺点:
1)开启线程须要占用必定的内存空间(默认状况下,主线程占用1M,子线程占用512KB),若是开启大量的线程,会占用大量的内存空间,下降程序的性能。
2)线程越多,CPU在调度线程上的开销就越大。
3)程序设计更加复杂:好比线程之间的通讯、多线程的数据共享安全
1)一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”。
2)做用。刷新显示UI,处理UI事件。多线程
1)不要将耗时操做放到主线程中去处理,会卡住线程。
2)和UI相关的刷新操做必须放到主线程中进行处理。
并发
技术方案 | 简介 | 语言 | 线程生命周期管理 | 使用频率 |
---|---|---|---|---|
pthread | —> 一套通用的多线程API —> 适用于Unix\Linux Windows等系统 —> 跨平台\可植入 —> 使用难度比较大 |
C | 手动 | 极低 |
NSThread | —> 使用更加面向对象 —> 简单易用,能够直接操做线程对象 |
Objective-C | 手动 | 底 |
GCD | —> 旨在替代NSThread等线程技术 —> 充分利用设备的多核 |
C | 自动 | 高 |
NSOperation | —> 基于GCD (底层是GCD) —> 比GCD多了一些更简单实用的功能 —> 使用更加面向对象 |
Objective-C | 自动 | 高 |
//pthread须要包含头文件 //1.建立线程对象 pthread_t thread; NSString *name = @"线程"; //2.使用pthread建立线程 //第一个参数:线程对象地址 //第二个参数:线程属性 //第三个参数:指向函数的指针 //第四个参数:传递给该函数的参数 pthread_create(&thread, NULL, run, (__bridge void *)(name));
// 第一种:alloc init,须要手动开启线程,能够拿到线程对象进行详细设置 NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"线程"]; [thread start]; // 第二种:分离(detach)出一条子线程,自动启动线程,没法对线程进行更详细的设置 [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"分离出的子线程"]; // 第三种:后台线程,自启动,不能详细设置 [self performSelectorInBackground:@selector(run:) withObject:@"后台线程"];
thread.name = @"线程A"; // 线程名称 thread.threadPriority = 1.0; // 线程的优先级,取值范围0.0~1.0,1.0的优先级最高,默认0.5
// 线程的各类状态:新建-就绪-运行-阻塞-死亡 // 经常使用的控制线程状态的方法 [NSThread exit]; // 退出当前线程 [NSThread sleepForTimeInterval:2.0]; // 阻塞线程 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];//阻塞线程 // 注意:线程死了不能复生
01 前提:多个线程访问同一块资源会发生数据安全问题 02 解决方案:加互斥锁 03 相关代码:@synchronized(self){} 04 专业术语-线程同步 05 原子和非原子属性(是否对setter方法加锁)
-(void)touchesBegan:(nonnull NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event { // 开启子线程下载图片 [NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil]; } -(void)downloadImage { NSURL *url = [NSURL URLWithString:@"http://p6.qhimg.com/t01d2954e2799c461ab.jpg"]; UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]; // 回到主线程刷新UI,如下三种任选其一,第二种方面些 [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES]; [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES]; [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES]; }
//第一种方法 NSDate *start = [NSDate date]; NSData *data = [NSData dataWithContentsOfURL:url]; NSDate *end = [NSDate date]; NSLog(@"耗时%f",[end timeIntervalSinceDate:start]); //第二种方法 CFTimeInterval start = CFAbsoluteTimeGetCurrent(); NSData *data = [NSData dataWithContentsOfURL:url]; CFTimeInterval end = CFAbsoluteTimeGetCurrent(); NSLog(@"耗时%f",end - start);
主队列:dispatch_get_main_queue()app
并发队列:dispatch_get_global_queue(0, 0),dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT)异步
手动建立串行队列:dispatch_queue_create("com.test", DISPATCH_QUEUE_SERIAL)async
并发队列 | 手动建立的串行队列 | 主队列 | |
---|---|---|---|
dispatch_sync | 没有开启新的线程,串行执行 | 同← | 同← |
dispatch_async | 有开启新的线程,并发执行 | 有开启新的线程,串行执行 | 没有开启新的线程,串行执行 |
// 获取一个全局的队列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 先开启一个线程,把下载图片的操做放在子线程中处理 dispatch_async(queue, ^{ // 下载图片 NSString *urlString = @"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"; NSURL *url = [NSURL URLWithString:urlString]; UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]; // 回到主线程刷新UI dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = image; }); });
—> 栅栏函数(控制任务的执行顺序)函数
如下实例,会保证Task One和Task Two所有完成后执行以后的Task Three和Task Four
dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ for (int i = 0; i < 10; i++) { NSLog(@"Task One"); } }); dispatch_async(queue, ^{ for (int i = 0; i < 10; i++) { NSLog(@"Task Two"); } }); // The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_async function. // 使用 dispatch_queue_create 函数建立并发队列(使用DISPATCH_QUEUE_CONCURRENT, 而不是DISPATCH_QUEUE_SERIAL), 不要使用dispatch_get_global_queue, 不然和dispatch_async同样效果 dispatch_barrier_async(queue, ^{ NSLog(@"++++++++++"); }); dispatch_async(queue, ^{ for (int i = 0; i < 10; i++) { NSLog(@"Task Three"); } }); dispatch_async(queue, ^{ for (int i = 0; i < 10; i++) { NSLog(@"Task Four"); } });
—> 延迟执行(延迟而且能够控制在哪一个线程执行)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // code to be executed after a specified delay });
—> 一次性代码
-(void)once { // 整个程序运行过程当中只会执行一次(注意区分一次性代码和懒加载) // onceToken用来记录该部分的代码是否被执行过 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ }); }
—> 快速迭代(开多个线程并发完成迭代操做)
dispatch_apply(12, dispatch_get_global_queue(0, 0), ^(size_t index) { NSLog(@"SuperLog------ %zd",index); });
—> 队列组(同栅栏函数)
dispatch_queue_t queue = dispatch_queue_create("com.example", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ }); // 队列组中的任务执行完毕以后,执行该函数 dispatch_group_notify(group, queue, ^{ });
—> NSInvocationOperation
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil]; [operation start];
—> NSBlockOperation
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ // 在主线程中执行 NSLog(@"downloadOne--%@",[NSThread currentThread]); }]; // 增长操做,增长的操做在子线程中执行 [operation addExecutionBlock:^{ NSLog(@"downloadTwo--%@",[NSThread currentThread]); }]; [operation addExecutionBlock:^{ NSLog(@"downloadThree--%@",[NSThread currentThread]); }]; [operation start];
—> 自定义NSOperation
// 自定义的NSOperation, 经过重写内部的main方法实现封装操做 -(void)main { NSLog(@"--main--%@",[NSThread currentThread]); } // 使用 SPOperation *operation = [[SPOperation alloc] init]; [operation start];
GCD中的队列
串行队列:dispatch_queue_create("com.test", DISPATCH_QUEUE_SERIAL),dispatch_get_main_queue()
并发队列:dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT),dispatch_get_global_queue(0, 0)NSOperationQueue
主队列:[NSOperationQueue mainqueue] 放在主队列中的操做都在主线程中执行
非主队列:[[NSOperationQueue alloc] init] 并发和串行,默认是并发执行的
// 自定义NSOperation -(void)customOperation { // 建立队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 建立操做 SPOperation *operationOne = [[SPOperation alloc] init]; SPOperation *operationTwo = [[SPOperation alloc] init]; // 添加操做到队列中 [queue addOperation:operationOne]; [queue addOperation:operationTwo]; // You can call this method explicitly if you want to execute your operations manually. However, it is a programmer error to call this method on an operation object that is already in an operation queue or to queue the operation after calling this method. Once you add an operation object to a queue, the queue assumes all responsibility for it. // 有队列管理,不要手动调用 start // [operationOne start] } //NSBlockOperation - (void)block { // 建立队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 封装操做 NSBlockOperation *operationOne = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"operationOne--%@",[NSThread currentThread]); }]; NSBlockOperation *operationTwo = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"operationTwo--%@",[NSThread currentThread]); }]; [operationTwo addExecutionBlock:^{ NSLog(@"operationThree--%@",[NSThread currentThread]); }]; // 添加操做到队列中 [queue addOperation:operationOne]; [queue addOperation:operationTwo]; // 开发直接使用该方法便可,简单方便 [queue addOperationWithBlock:^{ NSLog(@"operationFour--%@",[NSThread currentThread]); }]; } // NSInvocationOperation - (void)invocation { // 建立队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 封装操做 NSInvocationOperation *operationOne = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downloadOne) object:nil]; NSInvocationOperation *operationTwo = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downloadTwo) object:nil]; // 把封装好的操做添加到队列中 [queue addOperation:operationOne]; [queue addOperation:operationTwo]; }
—> 设置最大并发数
NSOperationQueue *queue = [[NSOperationQueue alloc]init]; // 设置最大并发数, 该属性须要在任务添加到队列中以前进行设置 // 该属性控制队列是串行执行仍是并发执行, 1 串行 >1 并行 // 系统的最大并发数有个默认的值,为-1,若是该属性设置为0,那么不会执行任何任务 queue.maxConcurrentOperationCount = 2;
—> 暂停和恢复以及取消
//设置暂停和恢复 //暂停表示不继续执行队列中的下一个任务,暂停操做是能够恢复的 if (self.queue.isSuspended) { self.queue.suspended = NO; }else { self.queue.suspended = YES; } // 取消队列里面的全部操做 // 取消以后,当前正在执行的操做的下一个操做将再也不执行,并且永远都不在执行,就像后面的全部任务都从队列里面移除了同样 // 取消操做是不能够恢复的 [self.queue cancelAllOperations]; // 自定义NSOperation取消操做 -(void)main { //耗时操做 for (int i = 0; i<1000; i++) { NSLog(@"任务1-%d--%@",i,[NSThread currentThread]); } NSLog(@"+++++++++++++++++++++++++++++++++"); //苹果官方建议,每当执行完一次耗时操做以后,就查看一下当前队列是否为取消状态,若是是,那么就直接退出(为了提升程序的性能) if (self.isCancelled) { return; } }
—> 开子线程下载图片
NSOperationQueue *queue = [[NSOperationQueue alloc]init]; [queue addOperationWithBlock:^{ NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"]; UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]; // 主线程刷新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.imageView.image = image; }]; }];
—> 操做依赖(operationThree 依赖operationOne和operationTwo)
NSOperationQueue *queue = [[NSOperationQueue alloc]init]; NSBlockOperation *operationOne = [NSBlockOperation blockOperationWithBlock:^{ // Task One }]; NSBlockOperation *operationTwo = [NSBlockOperation blockOperationWithBlock:^{ // Task Two }]; NSBlockOperation *operationThree = [NSBlockOperation blockOperationWithBlock:^{ // Task Three }]; // 操做依赖 [operationThree addDependency:operationOne]; [operationThree addDependency:operationTwo]; // 添加操做到队列中执行 [queue addOperation:operationOne]; [queue addOperation:operationTwo]; [queue addOperation:operationThree];
使用create函数建立的并发队列和全局并发队列的主要区别: