今天被人问到了下多线程方面的一些知识,平时一直在用然后原 理不是理解得很透彻,所以今晚决定整理资料来个扫盲贴.开始前我们先来个基础知识讲解吧.
![Uploading 多线程_837987.jpg . . .]
首先开始我们先区别下进程与线程的区别.
***
进程:系统每个程序运行都需要一个进程,进程间是在其专用且
受保护的内存空间内独立的
线程:1)线程为进程能够正常运作提供了条件,且每个进程最少得一条线程.
2)所谓进程通俗讲就是我们系统运行的程序,进程的基本执行单元为线程.
线程串行:
在单线程中执行任务是需要排队的,你如果要执行多人任务,那么就只能按顺序执行这些任务;就像去超市排队结账,你要等别 人结账完才能轮到你
多线程
废话不多说先来了解下什么是多线程先
***基本概念***
多线程就是给单前的进程(APP/程序)开多条线程就是开几条水管好排水,每条水管执行不同的Task.
***线程的并行***
所谓并行就是你像烧水的时候可以顺便干点其它东西,大家互相互执行不同的东西
说白了就是你的CPU在多线线程中来回工作,就是这条线程干点那条线程干点
***多线程优点与缺点 ***
缺点:1)开多线程就是一个人在进行多个任务一样,这样CPU开销会很大,太多线程其实会影响性能的
2)这样程序会设计的比较复杂,在数据共享与线程(APP)间通信实现难度很大
3)加重内存开销(默认主线程是1M,子线程是512KB)
优点:1)目前CPU的处理效率很高都是多核多核一定程度上提高资源的利用率(内存/CPU)
2)提高程序执行的效率了,就是你烧开水去切菜一样提高效率节省时间.
在iOS开发中的用处
宝贝说再好还不如来来点实在的对吧.
a)主线程
1.一个APP运行后在iOS系统中默认就是在主线程中运行的我们把它称为"主线程"或者"UI线程"
2.刷新显示界面,处理用户交互事件
坑点!!!:1)如果是在对一些比较占用资源运行时间较长的耗时操作记住要开子线程,要不你的APP就卡死啦
2)和用户交互的刷新操作一定要放在主线程,要不一些执着的用户会以为贵公司APP垃圾辣鸡就卸了.宝宝心里苦
主题几种多线程实现方案
***程序员管理的生命***
pthread说明:恶心的C语言API,我就不想用了,用起来好难,线程的生死还要我们程序员觉得,一不小心就崩了程序崩了知道不.
特点:1)可用在Unix\Linux\Windows主流系统可以跨平台开发
2)通用的API就是大家都可以用,公共厕所
NSThread特点:1)这个东西我们偶尔使用
2)伟大的OOP类
3)还是得码农解决生死
***系统自动管理***
GCD特点:1)为了替代NSThread等需要人工判断生死的东西
2)为了不浪费多核的CPU资源
3)还是C语言,使用难道大大的难
NSOperation特点:1)OOP语言,系统会自动管理
2)基于GCD底层的,比GCD使用方便还新添一些简单的功能
重点了解OOP语言中的NSTHread
基本使用:创建的3种方式
1)自动启动,不能管理内容设置
[self performSelectorInBackground:@selector(run:) withObject:@"I am 后台线程"];
2)自动启动,分离出一条子线程,还是不能管理内容设置
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是分离出来的子线程"];
***推荐使用***
3)可以管理内容设置但是需要手动开启线程.
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"wendingding"];
//启动线程
[thread start];
其它一些属性使用:
1)属性名称:thread.name = @"快给我起名字";
2)优先级(默认0.5):thread.threadPriority = 1.0;
线程safety:1)多线程同时操作同一个数据会发生死锁情况,需要加互斥所,相关代码@synchronized(self){}
2)原子性与非原子属性(是否对setter方法加锁), 线程同步与同步线程区别
线程同步:多个线程操作同一个数据资源提供竞争的解决方案
同步线程:就是排队串行执行任务的线程
线程状态:[NSThread exit]//退出当前的线程
[NSThread sleepForTimeInterval:2.0];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
//:线程挂了是不能复生的
线程间通信:
-(void)touchesBegan:(nonnull NSSet *)touches withEvent:(nullable UIEvent *)event
{
// [self download2];
//开启一条子线程来下载图片
[NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];
}
-(void)downloadImage
{
//1.确定要下载网络图片的url地址,一个url唯一对应着网络上的一个资源
NSURL *url = [NSURL URLWithString:@"http://www.jianshu.com/p/e09c44c5ab12"];
//2.根据url地址下载图片数据到本地(二进制数据
NSData *data = [NSData dataWithContentsOfURL:url];
//3.把下载到本地的二进制数据转换成图片
UIImage *image = [UIImage imageWithData:data];
//4.回到主线程刷新UI
//4.1 第一种方式
// [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
//4.2 第二种方式
// [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
//4.3 第三种方式
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
}
计算代码时间:
1)CFTimeInterval start = CFAbsoluteTimeGetCurrent();
NSData *data = [NSData dataWithContentsOfURL:url];
CFTimeInterval end = CFAbsoluteTimeGetCurrent();
NSLog(@"操作花费的时间为%f",end - start);
2)NSDate *start = [NSDate date];
//2.根据url地址下载图片数据到本地(二进制数据)
NSData *data = [NSData dataWithContentsOfURL:url];
NSDate *end = [NSDate date];
NSLog(@"第二步操作花费的时间为%f",[end timeIntervalSinceDate:start]);
学习重点 GCD
基本知识:
1)同步/异步函数
2)队列/任务理解
使用重点:
01 异步函数+并发队列:开启多条线程,并发执行任务
02 异步函数+串行队列:开启一条线程,串行执行任务
03 同步函数+并发队列:不开线程,串行执行任务
04 同步函数+串行队列:不开线程,串行执行任务
05 异步函数+主队列:不开线程,在主线程中串行执行任务
06 同步函数+主队列:不开线程,串行执行任务(注意死锁发生)
07 注意同步函数和异步函数在执行顺序上面的差异
GCD中线程间通信:
//0.获取一个全局的队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.先开启一个线程,把下载图片的操作放在子线程中处理
dispatch_async(queue, ^{
//2.下载图片
NSURL *url = [NSURL URLWithString:@"http://www.baidu.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"下载操作所在的线程--%@",[NSThread currentThread]);
//3.回到主线程刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
//打印查看当前线程
NSLog(@"刷新UI---%@",[NSThread currentThread]);
});
});
GCD中其它法宝
1)栅栏函数(控制任务的执行顺序)
dispatch_barrier_async(queue, ^{
NSLog(@"栅栏函数");
});
2)延迟执行(延迟·控制在哪个线程执行)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"---%@",[NSThread currentThread]);
});
3)一次性代码(注意不能放到懒加载)
-(void)onceToken
{
//整个程序运行过程中只会执行一次
//onceToken用来记录该部分的代码是否被执行过
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"-----");
});
}
4)快速迭代(开多个线程并发完成迭代操作)
dispatch_apply(subpaths.count, queue, ^(size_t index) {
});
5)队列组(同栅栏函数)
//创建队列组
dispatch_group_t group = dispatch_group_create();
//队列组中的任务执行完毕之后,执行该函数
dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
6)进入群组和离开群组
dispatch_group_enter(group);//执行该函数后,后面异步执行的block会被gruop监听
dispatch_group_leave(group);//异步block中,所有的任务都执行完毕,最后离开群组
//注意:dispatch_group_enter|dispatch_group_leave必须成对使用