一、程序、进程、多线程的区别
程序:由源代码生成的可执行应用。(例如:QQ.app)
进程:一个正在运行的程序可以看做一个进程。(例如:正在运行的QQ就是一个进程),进程拥有独立运行所需的全部资源。
线程:程序中独立运行的代码段。(例如:接收QQ消息的代码)
二、iOS中实现多线程的方法
先简单介绍一下NSThread,通过NSThread创建一个线程对象:
第一种创建子线程的方法
//第一个参数 执行方法的对象是谁
//第二个参数 线程中执行的方法
//第三个参数 方法的参数
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(printNumber:) object:@123];
thread.name = @"计算线程";
//如果通过初始化方法,先创建一个线程对象,那么需要我们人为地开启此线程
[thread start];
第二种创建线程的方法 不需要手动开启执行方法 此方法相对于第一种方法来说 代码简单但是不如第一种灵活 因为第一种可以根据自己的需求在适当的时候 在我们需要执行方法的时候 开启
[NSThread detachNewThreadSelector:@selector(printNumber:) toTarget:self withObject:@123];
以下是iOS中实现多线程的主要方法
①.Grand Central Dispatch (GCD):是苹果公司开发的技术。以优化应用程序支持多核心处理器和其他的对称多处理系统的系统。GCD属于函数级的多线程,性能更高,功能也更加强大。GCD是苹果官方推荐的一种多线程方式它具有灵活功能强大灵活代码简洁效率高底层都是C语言的特点。使用GCD我们需要关注的就是任务和队列,GCD本身也是通过queue来实现多线程的其中队列分为两种队列:
1 串行队列serial queue任务再次对列中时挨个执行的
2 并发队列concurrent queue任务在此队列中是同时执行的
3 GCD获取主队列和全局队列.
主队列:dispatch_queue_t m = dispatch_get_main_queue();
全局队列:dispatch_queue_t g =dispatch_get_global_queue(0,DISPATCH_QUEUE_PRIORITY_DEFAULT);
4除了队列之外我们还需要决定使用什么函数来执行
4.1.异步函数具备开启子线程的能力
4.2同步函数不具备开启子线程的能力
异步并发 最常用的 开启多条子线程 任务同时执行 并发队列里面的任务数量CPU使用情况 以及内存消耗情况 决定了到底开启多少条子线程(最适合的数量)
-(void)asyncConcurrent{
//获取并发队列有两种方法
//1.获取全局的并发队列
//第一个参数为队列的优先级 通常情况下我们给定默认优先级 队列的优先级越高 那么他的调度次数就会越多
//第二个参数为预留参数 给 0
//dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2.创建并发队列
//最后这个参数决定了 创建的队列是 并发还是串行
dispatch_queue_t queue1 = dispatch_queue_create(“com.qqq.www", DISPATCH_QUEUE_CONCURRENT);
//异步函数
//将任务添加到并发队列中 异步执行
dispatch_async(queue1, ^{
NSLog(@"第一次 %@ %d",[NSThread currentThread],[NSThread isMainThread]);
});
dispatch_async(queue1, ^{
NSLog(@"第二次 %@ %d",[NSThread currentThread],[NSThread isMainThread]);
});
dispatch_async(queue1, ^{
NSLog(@"第三次 %@ %d",[NSThread currentThread],[NSThread isMainThread]);
});
dispatch_async(queue1, ^{
NSLog(@"第四次 %@ %d",[NSThread currentThread],[NSThread isMainThread]);
});
}
//异步串行 (有时会用) 开启一条子线程 任务挨个执行
-(void)asyncSerial{
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.lanou3G", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"第一次 %@ %d",[NSThread currentThread],[NSThread isMainThread]);
});
dispatch_async(queue, ^{
NSLog(@"第二次 %@ %d",[NSThread currentThread],[NSThread isMainThread]);
});
dispatch_async(queue, ^{
NSLog(@"第三次 %@ %d",[NSThread currentThread],[NSThread isMainThread]);
});
dispatch_async(queue, ^{
NSLog(@"第四次 %@ %d",[NSThread currentThread],[NSThread isMainThread]);
});
//如果在MRC下 释放队列
//dispatch_release(<#object#>)
}
②:NSObject
[self performSelectorInBackground:@selector(downLoadImage:) withObject:imageStr];
③:NSOperationQueue
这种实现多线程的方式 我们需要做的就是 创建队列 创建任务 将任务添加到队列里面去 队列就会为添加进来的任务开辟线程 执行任务 我们针对任务只需要专注人物封装的功能就可以 不需要管任务的执行顺序 执行时间等
//第一种创建任务的方式
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downLoadImage:) object:imageStr];
//创建操作队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//将操作添加到队列里面
//队列中的任务 先加到队列中的任务 肯定是先执行的 但是先执行的任务 不一定是先执行完的 执行任务开辟的线程数量 以及任务如何选取线程 这些都不需要我们管
[queue addOperation:op1];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
//封装的代码
NSLog(@"111122223333");
}];
[queue addOperation:op2];
三、下面介绍几个比较重要的方法
① 获得的是并行队列:
1.dispatch_get_global_Queue 2.dispatch_queue_create(“com.lanou.queue”,DISPATCH_QUEUE_CONCURRENT);
②更新页面只能在哪种线程中进行?
答案:UI的刷新只能在主线程,下面是在主线程刷新UI页面的方法
-(void)threadConnection{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:kStr]]];
//回到主线程刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
}
③ 延时执行的方法介绍
-(void)delayExcution{
//延迟执行的方法 可以在任何队列中 不管是主队列 串行队列 还是并发队列此方法都有效
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"5秒后打印");
});
}
④ 线程的依赖关系
NSBlockOperation*op1 = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@"下载图片当前线程:%@是否是主线程:%d",[NSThreadcurrentThread],[NSThreadisMainThread]);
}];
NSBlockOperation*op2 = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@"添加水印当前线程:%@是否是主线程:%d",[NSThreadcurrentThread],[NSThreadisMainThread]);
}];
NSBlockOperation*op3 = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@"图片合成当前线程:%@是否是主线程:%d",[NSThreadcurrentThread],[NSThreadisMainThread]);
}];
//当任务的执行需要先后顺序时,我们会给任务添加依赖关系
//当op1执行完成之后op2才会执行
[op2addDependency:op1];
//当op2执行完毕之后op3才会执行
[op3addDependency:op2];
NSOperationQueue*queue = [[NSOperationQueuealloc]init];
[queueaddOperations:@[op1,op2,op3]waitUntilFinished:YES];
//设置任务的最大并发数执行任务的子线程数量最大为多少条如果想让任务依次执行那么最大并发数就可以设置为1那么此时只有一条开启的子线程
queue.maxConcurrentOperationCount=1;
//当op3执行完毕之后要返回主线程刷新UI
NSBlockOperation*mainOP = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@"刷新UI界面当前线程:%@是否是主线程:%d",[NSThreadcurrentThread],[NSThreadisMainThread]);
}];
//刷新的任务依赖于op3
[mainOPaddDependency:op3];
//需要将mainOP添加到主队列
[[NSOperationQueuemainQueue]addOperation:mainOP];
⑤ 将自己开辟的线程添加到自动释放池
-(void)printNumber:(NSNumber*)number{
inta = number.intValue;
//每一个线程都应该拟配备一个自动释放池,主线程的自动释放池已经有系统内部加好了,而我们人为地创建的子线程也应该在线程方法的功代码执行之前为其添加自动释放池
@autoreleasepool{
for(inti =0; i < a; i++) {
NSLog(@"%@ %d",[NSThreadcurrentThread],[NSThreadisMainThread]);
NSLog(@"%d",i);
}
}
//结束当前线程
[[NSThreadcurrentThread]cancel];
}