1.多线程
多线程并不是真正的开辟了多个线程,而是处理器一会儿执行这个,一会又执行那个,处理器在多线程之间切换,给人一个假象好像有多个线程。
2、程序,进程、线程之间的关系
程序:有代码生成的可执行应用(如:QQ.app)
进程:就是正在运行的程序(如正在运行的QQ),进程拥有独立运行所需的所有资源
线程:程序中独立运行的代码段。(如:接收QQ消息的代码)
注意:一个进程是由一或多个线程组成。进程只负责资源的调度和分配,线程才是真正的执行单元,负责代码的执行。
3.线程
(1)单线程:只有一个主线程的程序。
主线程:每个正在执行的程序(进程)至少包含一个线程,这个线程就是主线程。主线程在程序启动时被创建,用于执行main函数。
负责执行程序的所有代码(UI展现以及刷新,网络请求,本地存储等),这些代码只能顺序执行,无法并发执行。
**注意:**UI刷新和添加必须写在主线程中
子线程:相对主线程来讲,开辟的新的线程。
子线程和主线程都是独立运行的单元,各自的执行互不影响,可以并发执行。
有效避免了代码阻塞,提高程序的运行性能。
A. iOS默认给主线程分配1M的空间,默认给子线程分配512K的空间(分配字节数必须是4K的整数倍),开辟的空间用来存放线程中为变量开辟的空间,一般情况下足够使用。
B. 与栈空间的使用方式不同,主线程和子线程共用一块堆空间
C. 程序入口处默认设置了自动释放池(main函数),由主线程负责执行代码,子线程开辟的内存不在主线程管理的范围内(线程间互不干扰,相互独立),所以子线为对象开辟的空间不会被自动释放,需手动释放(手动书写自动释放池)
(2)
多线程种类:
1)脱离线程:线程结束后被销毁(如下载)
2)非脱离线程:线程结束后被挂起,等待唤醒,不销毁(如:通话功能)
(主线程一定是非脱离线程)
iOS实现多线程的种类:
1)NSObject
2)NSThread
3)NSOperation、NSOperationQueue
4)GCD
注意:在MVC中属于M,不能直接使用,而是使用子类NSInvocationOperation或NSBlockOperation,它本身无主线程,子线程之分,可在任意线程中使用,通常 与NSOperationQueue结合使用
1)NSInvocationOperation封装了执行操作的target和要执行的action
2)NSBlockOperation封装了要执行的代码块
#pragma mark - NSObject 多线程方式(主线程)
[self performSelectorInBackground:@selector(calculate:) withObject:@"参数"];//withObject:需要传递的参数,不传参时就不用写
#pragma mark - NSThread 多线程方式
//方式一(需要开启)
// NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(calculate:) object:nil];
// [thread1 start];//手动开启
//方式二
[NSThread detachNewThreadSelector:@selector(calculate:) toTarget:self withObject:nil];
#pragma mark - NSOperation 多线程方式(只是一个操作,并不代表是主线程(calculate中输出1))
//方式一:NSInvocationOperation
NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(printNum) object:nil];
// [invocation start];//手动启动
//方式二
__weak typeof(self) temp = self;
NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
[temp printAnotherNum];
}];
//[block start];
//创建一个操作队列,向队列中添加操作(可添加多个不同的invocation或block),完成多任务同时执行
//一个队列可以放多个线程,一个队列也可放到某个线程中执行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
#warning 注意:一定提前设置好并发数(设置到后面的话不起作用)
[queue setMaxConcurrentOperationCount:2];//最大并发数为1,表示串行,一个一个执行
[queue addOperation:invocation];
[queue addOperation:block];
//子线程
- (void)calculate:(id)sender{
@autoreleasepool{ //释放池
NSArray *array = @[@1,@2,@3,@4,@5];
for (int i = 0; i < 23; i ++) {
array = [array arrayByAddingObjectsFromArray:array];
}
self.view.backgroundColor = [UIColor cyanColor];
NSLog( @"-----%@",sender);
NSLog( @"%d",[NSThread isMainThread]);//是不是主线程
}
}
可以判断是不是主线程:
NSLog( @"%d",[NSThread isMainThread]);//判断是不是主线程
4.GCD
GCD:(Grand Central Dispatch )属于函数级的多线程
1)任务:具有一定功能的代码段,一般是一个block或者函数
2)分发队列:以队列的方式工作 ,先进先出
3)会根据分发队列的类型,创建合适数量的线程执行队列中的任务。
(1)GCD中有两种队列
1)SerialQueue:一次只执行一个任务。用于同步访问特定的资源或数据,当创建多个SerialQueue时,虽然他们各自是同步执行的,但SerialQueue与SerialQueue之间是并发执行的。
SerialQueue能实现同步
2)Concurrent:可以不会ing发执行多个任务,但是遵守先进先出
(2)GCD功能
1)dispatch_async() //往队列中添加任务,任务会排队执行
2)dispatch_after() //往队列里添加任务,任务排队切且在延迟的时间点执行
3)dispatch_apply() //网队列添加任务,任务会重复执行N次
4)dispatch_group_async() //将任务添加到队列中,并添加分组标记
5)dispatch_group_notify() //将任务添加到队列中,当某个分组的所有任务执行完后,此任务才会执行
6)dispatch_barrier_async() //将任务添加到队列中,此任务执行的时候,其他任务停止执行(前提是自定义并行队列)
7)dispatch_once() //将任务添加到队列中,但任务在程序运行中值执行一次
8)dispatch_sync() //将任务添加到队列中,block 不执行完,下面代码不会执行
9)dispatch_async_f() //将任务添加到队列中,任务是函数非block
详情在以下代码注释中:
#pragma mark - GCD
//1.主队列:是串行队列
//2.全局队列:是并行队列
//3.自定义队列(串行队列,并行队列)
//主队列和全局队列都是单例,主队列串行,全局队列并行
#pragma 主队列
/*
//获取主队列(dispatch_queue_t 是所有队列的类型)
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//向主队列添加任务
dispatch_async(mainQueue, ^{
NSLog(@"第一个任务");
});
dispatch_async(mainQueue, ^{
NSLog( @"第二个任务");
});
dispatch_async(mainQueue, ^{
NSLog(@"第三个任务");
});
//设置延时执行(延时器) 不要给主队列(串行)添加延时器 (自动提示中,只要有snippet,则会自动完成很多空缺的内容)
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));//从当前开始,10秒后执行
dispatch_after(time, mainQueue, ^{
NSLog(@"这是十年后的你");
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), mainQueue, ^{
NSLog(@"这是5年后的你");
});
// dispatch_apply(3, mainQueue, ^(size_t t) {
//
// NSLog(@" BE HAPPY ! %zi",t);
// });
*/
#pragma 全局队列
/*
//获取全局队列
//DISPATCH_QUEUE_PRIORITY_DEFAULT 优先级(默认)
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//向队列中添加任务(并行)
dispatch_async(globalQueue, ^{
NSLog(@"111111111111111");
});
dispatch_async(globalQueue, ^{
NSLog(@"22222222222");
});
dispatch_async(globalQueue, ^{
NSLog(@"33333333333333");
});
//重复执行某一个任务
dispatch_apply(3, globalQueue, ^(size_t t) { //并行执行3遍
NSLog(@"重要的是说三遍 %zu",t);
});
//分组任务
dispatch_group_t group = dispatch_group_create();
//向分组中添加任务
dispatch_group_notify(group, globalQueue, ^{
NSLog(@"最后一个");
});//和添加到队列的位置有关,当处于队列末尾的时候,最后执行
dispatch_group_async(group, globalQueue, ^{
NSLog(@"第一分组第一小队");
});
dispatch_group_async(group, globalQueue, ^{
NSLog(@"第一分组第二小队");
});
dispatch_group_async(group, globalQueue, ^{
NSLog(@"第一分组第三小队");
});
dispatch_barrier_async(globalQueue, ^{
int sum = 0;
for (int i = 0; i < 635500000; i ++) {
sum += i;
}
NSLog(@"执行完成");
});
*/
//此函数添加到队列的任务,需要等其执行完后才可以执行其他队列的任务
//前提:自定义并行队列
//dispatch_barrier_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
#pragma 自定义队列
//自定义串行队列
/*
//第一个参数:队列表示符,可以获取到某一队列的标示符
//第二个参数:队列的类型(串行或者并行)
dispatch_queue_t serialQueue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
//添加task
dispatch_async(serialQueue, ^{
NSLog(@"第一个任务");
});
dispatch_async(serialQueue, ^{
NSLog(@"第二个任务");
});
dispatch_async(serialQueue, ^{
NSLog(@"第三个任务");
});
//获取队列的标示符
const char *str = dispatch_queue_get_label(serialQueue);
NSLog(@"%s",str);
*/
//自定义并行队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
NSLog(@"1");
});
//必须等sync(如同串行)执行完,余下的队列任务才可以执行
dispatch_sync(concurrentQueue, ^{
int sum = 0;
for (int i = 0; i < 63500000; i ++) {
sum += i;
}
NSLog(@"执行");
});
//必须等barrier(阻塞的意思)函数执行完,余下的队列任务才可以执行
dispatch_barrier_async(concurrentQueue, ^{
int sum = 0;
for (int i = 0; i < 63500000; i ++) {
sum += i;
}
NSLog(@"哈哈哈");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"3");
});
//GCD可以使用此函数,向队列中添加函数,让函数执行
//函数类型 void (*)(void *) 任何类型都可以传进来
dispatch_async_f(concurrentQueue, "string", function);//string 是function函数的参数
//dispatch_once添加的任务,在整个程序运行期间只会执行一次(重复添加无效)(只会打印出一个)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"哈哈哈");
});
dispatch_once(&onceToken, ^{
NSLog(@"哈哈哈");
});
dispatch_once(&onceToken, ^{
NSLog(@"哈哈哈");
});
}