1.多线程概述
程序:由源代码生成的可执行应用。(eg:QQ.app)
进程:一个正在运行的程序可以看做一个进程。(eg:正在运行的QQ就是一个进程),进程用用独立运行所需的全部资源。
线程:程序中独立运行的代码段。(eg:接收QQ消息的代码)
一个进程是由一或多个线程组成。进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行。
1.1单线程
每个正在运行的程序(即进程),至少包含一个线程,这个线程就叫主线程。
主线程在程序启动时被创建,用于执行main函数。
只有一个主线程的程序,称为单线程程序。
在单线程程序中,主线程负责执行程序的所有代码(UI展现以及刷新,网络请求,本地存储等等)。这些代码只能顺序执行,无法并发执行。
1.2多线程
拥有多个线程的程序,称作多线程程序。
iOS中允许用户自己开辟新的线程,相对于主线程来讲,这些线程,称作子线程。
可以根据需要开辟若干子线程。
子线程和主线程都是独立的运行单元,各自执行互不影响,因此能够并发执行。
1.3单线程和多线程的区别
单线程程序:只有一个线程,即主线程,代码顺序执行,容易出现代码阻塞(页面假死)。
多线程程序:有多个线程,线程间独立运行,能有效的避免代码阻塞,并且提高程序的运行性能。
注意:iOS中关于UI的添加和刷新必须在主线程中操作。
2.iOS平台下的多线程
2.1NSThread
方法:
//初始化一个子线程,但需要手动开启
- (id) initWithTarget : (id) target selector : (SEL)selector object :(id)argument
//初始化一个子线程,并自动开启
+ (void) detachNewThreadSelector : (SEL) aSelector toTarget : (id) aTarget withObject :(id) anArgument
//开启子线程
start [operation start]
//取消当前子线程
cancel [operation cancel]
源代码:
//手动开辟子线程
NSThread *thread = [[NSThread alloc] initWithTarget : self selector:@selector(sayHi) object:nil];
[thread start];
//使用NSThread和NSObject实现的开辟线程,系统会自动释放,关不关都行
[thread cancel]; 没有真正取消,而是给线程发送一个信号,通过这个信号进行取消的
[thread exit]; 直接将线程退出
//自动开启子线程
[NSThread detachNewThreadSelector:@selector(sayHi) toTarget:self withObject:nil];
self.view.backgroundColor = [UIColor redColor] ;
2.2NSObject
//使用performSelectorInBackground开辟子线程
[self performSelectorInBackground :@selector(sayHi) withObject:@"test"];
//在子线程调用的方法里返回主线程,在调用另一个方法
[self performSelectoraOnMainThread:@selector(mainThreadChangeColor) withObject :nil waitUntilDone: YES];
- (void)mainThreadChangeColor {
self.view.backgroudColor = [UIColor magentaColor];
}
方法:
获取当前线程:[NSThread currentThread]
获取主线程 :[NSThread mainThread]
线程休眠2秒 :[NSThread sleepForTimeInterval:2]
注意:
1.每个线程都维护这与自己对应的NSAutoreleasePool对象,将其放在线程栈的栈顶。当线程结束时,会清空自动释放池。
2.为保证对象的及时释放,在多线程方法中需要添加自动释放池。
3.在应用程序打开的时候,系统会自动为主线程创建一个自动释放池。
4.我们手动创建的子线程需要我们手动添加自动释放池。
4.NSOperation和NSOperationQueue
NSOperation类,在NVC中属于M,是用来封装单个任务相关的代码和数据的抽象类。
因为它是抽象的,不能够直接使用这个类,而是使用子类(NSInvocation,NSBlockOperation)来执行实际任务。
NSOperation(含子类),只是一个操作,本身无主线程,子线程之分,可在任意线程中使用。通常与NSOperationQueue结合使用。
//NSInvocationOperation:封装了执行操作的target和要执行的action
//NSBlockOperation:封装了需要执行的代码
//NSOperation不能直接进行多线程的创建,需要借助:NSOperationQueue
//在单独使用NSOperation的子类去创建线程的时候,一定要启动才行;
//在使用NSOperation的子类去创建线程的时候,实际上线程没有真正意义上的创建
源程序:
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test) object:nil];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"我是block");
NSLog(@"+++++%@",[NSThread currentThread]);
NSLog(@"*******%@",[NSThread mainThread]);
}];
- (void)test {
NSLog(@"?");
NSLog(@"currentThread == %@",[NSThread currentThread]);
NSLog(@"mainThread == %@",[NSThread mainThread]);
}
//借助队列,创建子线程
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
[queue addOperation:blockOperation]; //有addOperation的时候,就不可以用start,否则会造成崩溃
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//创建队列的对象
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//最大的并发数量值
//当值设置为1的时候,可以叫做串行(单线程)顺序执行
queue.maxConcurrentOperationCount = 2;
//当值设置大于1的时候,叫做并行:多条通道同时进行各自的任务
for (int i = 0; i < 10; i++) {
//创建10个线程
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"currentThread == %@,mainThread == %@,%d",[NSThread currentThread],[NSThread mainThread],i);
}];
[queue addOperation:blockOperation];
}
}
5.GCD(Grand Central Dispatch)使用
简介:GCD是Apple开发的一种多核编程技术。主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。
GCD提供函数实现多线程开发,性能更高,功能也更强大。
它首次发布在Mac OS X 10.6,iOS4及以上也可以用。
任务:具有一定功能的代码段。一般是一个block或者函数。
分发队列:GCD以队列的方式进行工作,FIFO(先进先出)
GCD会根据分发队列的类型,创建合适数量的线程执行队列中的任务。
GCD中两种队列:
1.SerialQueue:一次只执行一个任务。Serial queue通常用于通常用于同步访问特定的资源或数据。当你创建多个Serial queue时,虽然他们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。
2.Concurrent:可以并发地执行多个任务,但是遵守FIFO
#pragma mark - 使用GCD去创建一个串行队列
//第一种:系统提供的创建串行队列的方法
dispatch_queue_t queue = dispatch_get_main_queue();
//在真正的开发中如果需要创建串行队列,比较习惯用这种
//第二种:自己去创建
dispatch_queue_t = dispatch_queue_create(DISPATCH_QUEUE_SERIAL,0);
#pragma mark - 使用GCD去创建并行队列
//第一种:系统的方式
//参数1:优先级(有4个,没有明显的区别)
dispatch_queue_t queue = dispatch_get_globel_queue(DISPATCH_QUEUE_PRIORITY);
//第二种:自己定义的方式(参数和串行不一样)
//参数1:表示创建队列的名字
//参数2:系统提供的宏
//创建队列
dispatch_queue_t queue = dispatch_queue_create("myQueue",DISPATCH_QUEUE_CONCURRENT);
//创建任务
dispatch_async(queue,^{
NSLog(@"current == %@",[NSThread currentThread]);
NSLog(@"main == %@",[NSThread MainThread]);
NSLog(@"我是任务一,子进程一");
});
dispatch_async(queue,^{
NSLog(@"current == %@",[NSThread currentThread]);
NSLog(@"main == %@",[NSThread MainThread]);
NSLog(@"我是任务二,子进程二");
});
dispatch_async(queue,^{
NSLog(@"current == %@",[NSThread currentThread]);
NSLog(@"main == %@",[NSThread MainThread]);
NSLog(@"我是任务三,子进程三");
});
#pragma mark - 几秒之后去做每一件事
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(3.0 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
NSLog(@"3.0秒的时候");
}
);
#pragma mark - 重复向一个队列中添加多个任务
dispatch_queue_t queue = dispatch_queue_create(0,DISPATH_QUEUE_CONCURRENT);
dispatch_apply(100,queue,^(size_t index)) {
NSLog(@"index = %zu",index);
}
#pragma mark - 分组
//创建一个分组
dispatch_group_t group = dispatch_group_create();
//创建一个并行队列
dispatch_queue_t queue = dispatch_queue_create(0,DISPATCH_QUEUE_CONCURRENT);
//创建任务1
dispatch_group_asunc(group,queue,^{
NSLog(@"我是任务1");
});
//创建任务2
dispatch_group_asunc(group,queue,^{
NSLog(@"我是任务2");
});
//创建任务3
dispatch_group_asunc(group,queue,^{
NSLog(@"我是任务3");
});
//用于监听所有任务的执行情况的,所以此功能代码必须放在所有任务之后进行书写的
dispatch_group_notify(group,queue,^{
NSLog(@"我是监听的,最后执行");
});
#pragma mark - 并发中的串行(披着羊皮的狼)
//创建的串行队列
dispatch_queue_t queue = dispatch_queue_create(0,DISPATH_QUEUE_CONCURRENT);
dispatch_async(queue,^{
NSLog(@"任务1");
});
dispatch_async(queue,^{
NSLog(@"任务2");
});
dispatch_async(queue,^{
NSLog(@"任务3");
});
#pragma mark - loadData
//1.url
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
//2.session
NSURLSession *session = [NSURLSession sharedSession];
//3.task
NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *_NUllabel data,NSURLResponse *_Nullabel response,NSError *_Nullable error){
if(error == nil){
//处理数据
//回到主线程刷新UI
dispatch_async(dispatch_get_main_queue(),^{
//自己灵活的写点东西吧!!!
})
}
}];