什么是线程?什么是主线程?什么是子线程?
在我们程序运行期间,每个正在运行的代码段,被称为线程。
程序运行期间,至少有一个线程,该线程为主线程,主要负责刷新UI,比如添加控件,删除控件,更新控件等。
如果遇到耗时的功能,程序中有多个线程,除了主线程,其余的都是子线程。
子线程主要负责,耗时的任务,比如,网络请求,处理大数据等。
iOS 如果遇到多个耗时的任务,或者要求,多个任务同时执行。我们可以使用多线程编程技术,开辟多个线程,把任务放到其他进程中执行。
线程与线程之间独立执行,互不干扰。
//iOS 中多线程方式
一,NSTread 线程类
1,使用线程类,来开辟线程
二,NSobject分类方法
使用场景:只想要一个操作,在开辟的子线程中执行。
[self performSelectorInBackground:@selector(printString;) withObject:@"分类开辟子线程方法"];
[self performSelectorInBackground:@selector(printString;) withObject:@"分类开辟子线程方法1"];
[self performSelectorInBackground:@selector(printString;) withObject:@"分类开辟子线程方法2"];
[self performSelectorInBackground:@selector(printString;) withObject:@"分类开辟子线程方法3"];
// 在主线程中执行SEL方法,传递参数,方法执行结束之前是否等待 yes 等待 ,no不等待
// @"111"是作为参数传递进 方法printString:
[self performSelectorOnMainThread:@selector(printString:) withObject:@"111" waitUntilDone:YES];
主线程主要用来接受用户相应的操作,更新UI,因此,我们添加UI,更新UI,以及删除UI的操作,一定要放在主线程中执行。比如:label.text = @"aaa";
// 四 , GCD
GCD属于函数级别的多线程,主要是分配不同功能的队列,提供给用户使用。
因此,我们在使用GCD时,根据需求,选用不同功能的队列解决问题。
//1. gcd 串行队列
- (IBAction)serialQueue:(id)sender
{
// 何时使用: 当多个任务具有依赖关系的时候。
// 比如:处理用户输入-》网络请求-》处理请求结果 -》登录
//1.创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("queue",DISPATCH_QUEUE_SERIAL);
//2.添加任务,任务只能是block或者是函数类型
// 两个参数,前面一个是队列名,后面是添加的任务,代码块block
dispatch_async(queue,^{
NSLog(@"%@",[NSThread currentThread];
NSLog(@"I was borned");
});
dispatch_async(queue,^{
NSLog(@"%@",[NSThread currentThread];
NSLog(@"i growed");
});
dispatch_async(queue,^{
NSLog(@"%@",[NSThread currentThread];
NSLog(@"I married");
});
dispatch_async(queue,^{
NSLog(@"%@",[NSThread currentThread];
NSLog(@"You were dead");
});
// 2.全局串行队列是main函数执行所在的主线程的队列,我们称为主队列。主队列是一个全局串行队列,便于用户访问
// 获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// 给主队列添加刷新UI的操作
dispatch_async(mainQueue,^{
self.view.backgroundColor = [UIColor redColor];
});
}
// 并行队列 conCurrentQueue
- (IBAction)conCurrentQueue:(id)sender
{
// 并行队列是一种同时可以有多个任务并发执行的队列
// 使用方式:当多个任务,没有依赖关系,谁先执行,谁后执行,都不重要。
//1. 创建并行队列
dispatch_queue_t currentQueue = dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);
// 2.提交操作
dispatch_async(currentQueue,^{
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"猴子一 连入游戏");
}
dispatch_async(currentQueue,^{
// block 块内封装的可以是需要执行的任务
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"猴子二 连入游戏");
}
dispatch_async(currentQueue,^{
// block 块内封装的可以是需要执行的任务
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"猴子三 连入游戏");
}
}
// 组提交方式
- (IBAction)group:(id)sender
{
// 什么是组提交方式
// 某些时候,我们需要把一些操作,归为一类操作,比如,链接操作,读取操作。而链接操作和读取操作中,又有多个操作。此时使用组提交方式。
//1、创建一个并行队列
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
//2、创建一个组队列
dispatch_group_t group1 = dispatch_group_create();
//3、提交
dispatch_group_async(group1, queue1, ^{
NSLog(@"玩家1连入游戏");
});
dispatch_group_async(group1, queue1, ^{
NSLog(@"玩家2连入游戏");
});
dispatch_group_async(group1, queue1, ^{
NSLog(@"玩家3连入游戏");
});
dispatch_group_async(group1, queue1, ^{
NSLog(@"玩家4连入游戏");
});
dispatch_group_async(group1, queue1, ^{
NSLog(@"玩家5连入游戏");
});
// 组中通知提交方式,当组中所有其他操作执行完毕后,它才会执行
dispatch_group_notify(group1,queue1,^{
NSLog(@"所有玩家接入完毕,游戏开始");
});
}
// barrier 障碍提交方式
- (IBAction)barrier:(id)sender
{
// barrier 提交方式,其实是把多个操作,划分为两个部分,这两个部分执行方式都是并发执行的。
// 只要多一个barrier 就会划分出来两个部分
// 通常用于前面有多个操作,后面有多个操作,后面部分要等到前面部分执行完毕再执行
//1. 并行执行
dispatch_queue_t queue = dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);
//2.提交操作
dispatch_async(queue,^{
NSLog(@"网络连接成功");
});
dispatch_async(queue,^{
NSLog(@"用户请求成功“);
});
dispatch_async(queue,^{
NSLog(@"验证用户名密码");
});
// 障碍提交方式
dispatch_barrier_async(queue,^{
NSLog(@"验证完成");
});
dispatch_async(queue,^{
NSLog(@"读取用户数据");
});
}
// after 延迟执行
- (IBAction)after:(id)after{
// 延迟执行,多用于加载图,线程睡眠等使用场景
// 1.从哪个时间点去
// 2. 过几秒后
// 3.提交到哪个队列中
// 4. 执行block
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(5*NSEC_PER_SEC)) , dispatch_get_main_queue(),^{
self.view.backgroundColor = [UIColor redColor];
// 延迟五秒,把当前控制器所在的视图的背景颜色换为红色
});
}
// apply 重复提交执行
- (IBAction)apply:(id)sender
{
// 使用场景:有多个任务,需要提交到队列中时,使用apply方式
//1. 把多个任务存储到数组中
NSArray *array = @[@"图片1",@"图片2",@"图片3",@"图片4",@"图片5"];
//2. 提交到一个并行队列中
// 参数1. 数组个数,重复执行次数
// 参数2.提交到哪个队列中
// 参数3.当前执行的次数
dispatch_apply(5,dispatch_get_global_queue(0,0),^(size_t i){
// i从0开始到 5-1
NSLog(@"%@",array[i]);
});
}
// NSOperationQueue
- (IBAction)operationQueue:(id)sender
{
// 使用场景,当我们有多个任务,都需要放倒子线程中执行时,使用操作队列这种方式,更为高效,队列内部给我们创建了合适的线程数。
// 什么是操作队列? 何时使用操作队列?
// 操作队列事以一个队列,内部提交的是操作对象,队列中可以给我们开辟适当的线程数去解决多个要执行的操作
// 操作队列是以合适的线程数,更加高效地解决需求
// 创建操作对象
NSInvocationOperation *invo1 = [[NSInvovationOperation alloc] initWithTarget:self selector:@selector(printString:) object:@"1111"];
NSInvocationOperation *invo2 = [[NSInvovationOperation alloc] initWithTarget:self selector:@selector(printString:) object:@"2222"];
NSInvocationOperation *invo3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(printString:) object:@"333"];
// 2.以block方式创建对象
NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{
// block 内部填写我们要执行的操作,当该操作对象执行时,会调用block,执行操作
NSLog(@"I");
}];
NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{
// block 内部填写我们要执行的操作,当该操作对象执行时,会调用block,执行操作
NSLog(@"lOVE");
}];
NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{
//block 内部填写我们要执行的操作,当该操作对象执行时,会调用block,执行操作
NSLog(@"You");
}];
// 1. 创建一个操作队列
NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
// 设置操作队列的最大并发数
queue1.maxConcurrentOperationCount = 3;
// 同一时间点,能够执行的操作数
// 当队列的最大并发数为1时为,串行队列,大于1为并行队列。
// 如何让并行队列中,某些任务按照顺序执行?
// 给任务添加依赖关系
[invo2 addDependency:invo1];
[invo3 addDependency:invoke];
//把操作添加到操作队列里面
// [queue1 addOperation:invo1];
// [queue1 addOperation:invo2];
// [queue1 addOperation:invo3];
//把操作添加到操作队列里面
[queue1 addOperation:bo1];
[queue1 addOperation:bo2];
[queue1 addOperation:bo3];
}
- (IBAction)block:(id)sender {
// [self printNumber];
[NSThread detachNewThreadSelector:@selector(printNumber) toTarget:self withObject:nil];
}
- (IBAction)thread:(id)sender {
//1、开辟一个线程
// [NSThread detachNewThreadSelector:@selector(printNumber) toTarget:self withObject:nil];
// 注意: detach 创建的线程,自动执行,通常用于默认执行的操作。
// 2、NSThread 开辟的线程另一种方法 init
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(printString:) object:@"111" ];
//初始化方法创建的线程,需要我们手动开启,使用start方法
[thread start];
//结束线程
// [thread cancel];
NSLog(@"%@ %ld",[NSThread currentThread],[NSThread isMainThread]);
}
// 打印一个string
- (void)printString:(NSString *)string
{
NSLog(@"%@ %d %@",[NSThread currentThread ],[NSThread isMainThread],string);
}
- (IBAction)cankao:(id)sender {
NSLog(@"aaaaa");
}
//for 循环方法
- (void)printNumber
{
@autoreleasepool {
for (int i = 0; i < 655000000; i++) {
NSLog(@"%d",i);
}
}
}
- (IBAction)sync:(id)sender {
//同步提交方式会等 提交的操作执行完毕后,再执行后面的操作。
//同步提交方式会卡主线程。
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@",[NSThread currentThread]);
for (int i = 0; i < 10; i ++) {
NSLog(@"%d",i);
}
});
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"我在下面我在下面我在下面");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"aaaaaaaaa");
});
}
void sum(void *string)
{
//注意传递过来的是什么类型就用什么类型打印。
NSLog(@"%s",string);
}
// 提交函数
- (IBAction)function:(id)sender {
dispatch_async_f(dispatch_get_main_queue(), "aaa", sum);
//获取线程名
dispatch_queue_t queue = dispatch_queue_create("aaa", DISPATCH_QUEUE_CONCURRENT);
const char *name = dispatch_queue_get_label(queue);
NSLog(@"%@",[NSString stringWithUTF8String:(const char *)name]);
}