多线程原理:
- 同一时间CPU只能处理一条线程, 只有一条线程在工作
- 多线程并发执行,起始是CPU在各个线程之间快速调度的结果
- 由于CPU调度线程速度非常快,所以就造成了多线程并发的假象
一般情况下耗时操作放在子线程里面,多线程也正是解决耗时操作,防止卡住主线程产生的。
主线程
- 程序已启动就自动创建的线程就是主线程
- 作用一般就是相应用户点击事件,刷新UI等等
多线程的实现方案
Paste_Image.png- NSThread
@Parmark 第一中创建方法
//创建线程
//在内存中开辟空间
NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"thread"];
thread.name = @"my-thread";
//启动线程 并且系统会把这个线程放到可调度程序池里面 为了方便CPU来回调用
[thread start];
//任务执行完毕后 会自动销毁线程
- (void)run:(NSString *)param
{
//处理耗时操作
}
@Parmark 第二中创建方法
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"thread"];
- (void)run:(NSString *)param
{
//处理耗时操作
}
@Parmark 第三中创建方法
[self performSelectorInBackground:@selector(run:) withObject:@"thread"];
- (void)run:(NSString *)param
{
//处理耗时操作
}
//卡住线程睡两秒 控制线程进入阻塞状态
[NSThread sleepForTimeInterval:2.0];
//退出线程(强制性的)
[NSThread exit];
线程安全
- 资源共享:一块资源 被多个线程共享 也就是多个线程访问同一块资源
隐患:
Paste_Image.png解决引号 -- > 加把互斥锁
Paste_Image.png代码:
###没加互斥锁的代码
self.ticketCount = 100;//100张票
self.thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread1.name = @"售票员1";
self.thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread2.name = @"售票员2";
self.thread3 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread3.name = @"售票员3";
- (void)saleTicket
{
while (1) {
//先取出总数
NSInteger count = self.ticketCount;
if (count > 0) {
self.ticketCount = count - 1;
NSLog(@"%@卖了一张票 -- 还剩下%ld张票",[NSThread currentThread].name,_ticketCount);
}else{
NSLog(@"票已经卖完了");
break;
}
}
}
###加互斥锁的代码
- (void)saleTicket
{
//用同一把锁 可以记录线程的状态
while (1) {
@synchronized (self) {//加锁
//先取出总数
NSInteger count = self.ticketCount;
if (count > 0) {
self.ticketCount = count - 1;
NSLog(@"%@卖了一张票 -- 还剩下%ld张票",[NSThread currentThread].name,_ticketCount);
}else{
NSLog(@"票已经卖完了");
break;
}
}
}
}
线程间的通讯
//开辟一个子线程
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelectorInBackground:@selector(downLoad) withObject:nil];
}
//子线程要做的事
- (void)downLoad
{
NSURL * url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/pic/item/5ab5c9ea15ce36d358d27ee43ef33a87e850b114.jpg"];
//下载图片
NSData * data = [NSData dataWithContentsOfURL:url];
//生成图片
UIImage * image = [UIImage imageWithData:data];
//回到主线程刷新UI
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
// [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
}
GCD的基本使用
- 任务: 执行操作
- 队列: 存放任务
- 队列的类型
- 并发队列 (只有在)dispatch_async下才有效
- 串行队列
同步 - 异步:主要影响是能不能开新线程
- 同步只能在当前线程执行任务,不能开启新线程
- 异步可以在新的线程中执行任务,能开启新线程
串行 - 并发:主要影响任务的执行方式
- 串行:一个任务执行完毕 执行下一个任务
- 并发:多个任务同时执行
使用步骤
- 定制任务:确定想要做的事情
- 将任务添加到队列中
- GCD会自动将队列中的任务取出来,放到对应的线程中执行
- 任务的去除遵循:先进先出 后进后出 的原则
代码:
//异步线程
dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
//同步线程
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
###异步函数+并发队列 (可以开启多条线程 并且可以同时执行)
//创建一个并发队列 DISPATCH_QUEUE_CONCURRENT:队列类型
dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_CONCURRENT);
//将任务加入队列
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
#@pargam 或者这种写法
//获取全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//将任务加入队列
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
###同步函数+并发队列 (不会开启新的线程)
//获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//同步函数
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
###异步函数+串行对列 (可以开线程 但是不能同时执行)
//串行队列 没有全局的 只能手动创建
dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
###同步函数+串行对列 (不可以开线程 )
//串行队列 没有全局的 只能手动创建
dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
主队列
- GCD自带的一种特殊的串行队列
- 放到主队列的任务都会,在主线程中执行
- 使用dispatch_get_main_queue()获取主队列
###主队列+异步函数(只会在主线程中执行任务)
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
###主队列+同步函数(线程冲突 不会执行任何操作)
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
各种队列的执行效果
Paste_Image.pngGCD的线程之间通讯
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL * url = [NSURL URLWithString:@"https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1479864304&di=99dcf40127f2dc4273536f73d0951638&src=http://d.hiphotos.baidu.com/image/pic/item/2e2eb9389b504fc2065e2bd2e1dde71191ef6de0.jpg"];
NSData * data = [NSData dataWithContentsOfURL:url];
UIImage * image = [UIImage imageWithData:data];
//回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
GCD中还有另外一个执行任务的函数
//在它前面的函数执行完毕才执行它的任务,在它后面的函数在它执行完毕后才开始执行
dispatch_barrier_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
iOS延时执行的函数
//延时两秒执行run方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//执行的操作
});
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//一次性函数
//此函数只执行一次
});
GCD队列组
- 用队列组下载多张图片并且合成一张图片
//创建一个队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建一个队列组
dispatch_group_t group = dispatch_group_create();
//1.下载图片1
dispatch_group_async(group, queue, ^{
NSURL * url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/h%3D200/sign=4241e02c86025aafcc3279cbcbecab8d/562c11dfa9ec8a13f075f10cf303918fa1ecc0eb.jpg"];
NSData * data = [NSData dataWithContentsOfURL:url];
UIImage * image = [UIImage imageWithData:data];
self.image1 = image;
});
//2.下载图片2
dispatch_group_async(group, queue, ^{
NSURL * url = [NSURL URLWithString:@"http://e.hiphotos.baidu.com/image/pic/item/8cb1cb134954092359d94e479758d109b3de4952.jpg"];
NSData * data = [NSData dataWithContentsOfURL:url];
UIImage * image = [UIImage imageWithData:data];
self.image2 = image;
});
//3.将图片1和图片2合成一张新的图片
dispatch_group_notify(group, queue, ^{
//能保证组里面的任务都完成了
//能来到这里说明前两张图片一定下载完了
//开启图形上下文
UIGraphicsBeginImageContext(CGSizeMake(336, 440));
//绘制图片
[self.image1 drawInRect:CGRectMake(0, 0, 168, 220)];
[self.image2 drawInRect:CGRectMake(168, 0, 168, 220)];
//获取上下文的图片
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
//结束上下文
UIGraphicsEndImageContext();
//回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
//4.将合成后的图片显示出来
GCD实现单利
###第一种方式
static Person * _person;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone: zone];
});
return _instance;
}
+ (instancetype)defaultManger
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_person = [[self alloc]init];
});
return _person;
}
//记得遵守NSCopying协议
//实现此方法为了保证copy的时候 访问的是同一个对象
- (id)copyWithZone:(NSZone *)zone
{
return _person;
}
###第二种方式
static id _instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
@synchronized (self) {//加锁 防止多线程访问出问题
if (_instance == nil)
{
_instance = [self allocWithZone:zone];
}
}
return _instance;
}
+ (instancetype)sharedInstance
{
@synchronized (self) {//加锁 防止多线程访问出问题
if (_instance == nil)
{
_instance = [[self alloc]init];
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
网友评论