iOS开发 - 多线程
Posted 酷睿石头cry4tal
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发 - 多线程相关的知识,希望对你有一定的参考价值。
知识点
1.理解线程的概念
2.NSThread的使用
3.NSOperation的使用
4.GCD的使用
5.线程锁,线程安全
===============================
1.多线程是一种实现多任务并发执行的技术,允许同时执行多个任务,能够更合理的利用CPU的资源,提高效率、防止用户界面卡顿。
在ios中,所有的UI处理只能在主线程做。
什么是进程?
· 简单的说进程就是我们电脑上运行的一个个应用程序,每一个程序就是一个进程,并且每个进程之间是独立的,每个进程运行在其专用受保护的内存空间内(window系统可以通过任务管理器进行查看,Mac系统中可以通过活动监视器对其进行查看)
什么是线程?
· 通过上面的介绍我们知道了什么是进程,那么如何让进程运行起来,这个时候就要有线程了,也就是说每个应用程序想要跑起来,最少也要有一条线程存在,其实应用程序启动的时候我们的系统就会默认帮我们的应用程序开启一条线程,这条线程也叫做’主线程’,或者’UI线程’
进程和线程之间的关系
· 线程是进行的执行单元,进程的所有任务都在线程中执行,举例来说:进程就好比公司中的一个个部门,线程则代表着部门中的同事,而主线程当然是我们的老板了,一个公司部能没有老板,一个程序不能没有线程其实都是一个道理.
什么是CPU?
· CPU(中央处理器,Central Processing Unit)是一块超大规模的集成电路,只要用来解释计算机指令以及处理计算机软件中的数据.
多线程的原理
· 同一时间,CPU值能处理一个线程,只有一条线程在执行,多线程指的就是多条线程同时执行,其实就是CPU快速的在多条线程之间的切换,如果CPU调度线程的时间足够快,那么就会造成多线程并发执行的假象,而当线程特别多的时候,那么CPU在多条切换的效率也就会下降,同时消耗大量的CPU资源,线程的执行效率也就会下降.
多线程优点
· 能适当提高程序的执行效率
· 能适当提高资源的利用率
多线程的缺点
· 开启线程需要占用一定的内存空间,如果开启大量的线程,则会占用大量的内存空间,降低程序的性能
· 线程越多,CPU在调度线程上得开销就越大,程序的设计上也就更加的复杂,因此不推荐开启太多的线程,一般开启2~5条为最佳(且用且珍惜);
主线程
· 也就是应用程序启动的时候,系统默认帮我们创建的线程,称之为’主线程’或者是’UI线程’;
· 主线程的作用一般都是用来显示或者刷新UI界面例如:点击,滚动,拖拽等事件
===============================
2.NSThread线程控制
1).创建线程,并自动执行
[NSThread detachNewThreadSelector:@selector(doSomeThing) toTarget:self withObject:nil];
2).创建线程,不自动执行
[[NSThread alloc] initWithTarget:self selector:@selector(doSomeThing) object:nil];
3).设置线程名
thread.name = @"另一个线程";
4).执行线程
[thread start];
5).线程取消
[thread cancel];
6).函数内获取当前线程
[NSThread currentThread];
7).获取主线程
[NSThread mainThread];
7).线程休眠
[NSThread sleepForTimeInterval:1.0f]; // 休眠几秒
[NSThread sleepUntilDate:date]; // 休眠到指定时间
8).线程退出
[NSThread exit];
9).线程通信
[self performSelector:@selector(function) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES];
===============================
3.NSOperation,是以任务为中心的一种多线程技术,并不直接管理线程
1).NSOperation是抽象父类,不要直接使用,而应该使用它的子类
NSInvocationOperation
[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomeThing) object:nil];
NSBlockOperation
[NSBlockOperation blockOperationWithBlock:^{}];
添加任务间的依赖关系,前者依赖于后者的完成,也就是后者执行完,前者才能执行,依赖关系需要放在添加到队列之前设置
[invocation addDependency:blockOperation];
如果有必要,可以让Operation取消
[invocation cancel];
2.NSOperationQueue,任务队列,NSOperation对象需要添加到队列里面才会执行
添加到队列里之后,自动会给每个NSOperation对象创建一个线程去执行
创建NSOperationQueue
[[NSOperationQueue alloc] init];
设置最大并发数
queue.maxConcurrentOperationCount = 1;
添加任务到队列里
[queue addOperation:blockOperation];
让队列里面,所有的Operation都取消
[queue cancelAllOperations];
获取当前线程对列
currentQueue
获取主线程对列
mainQueue
===============================
4.GCD是一套C语言的线程控制API,是NSOperation的底层实现,用Block来表示一个任务
1).创建队列
dispatch_queue_create(“QF Queue”, DISPATCH_QUEUE_CONCURRENT);
2).系统的队列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 全局队列
dispatch_get_main_queue(); // 主线程队列
3).异步执行任务
dispatch_async(globalQuque, ^{});
4).创建分组
dispatch_group_create();
5).添加任务到分组,并执行
dispatch_group_async(group, globalQuque, ^{});
6).分组执行完的通知
dispatch_group_notify(group, mainQuque, ^{});
===============================
5.线程锁
1).方式1:
_lock = [[NSLock alloc] init];
[_lock tryLock];
[_lock unlock];
2).方式2:
@synchronized(self) {}
NSThread
//多线程 -- 为了将一些耗时的操作放在多线程中去执行 主要是为了给一个好的用户体验,防止卡顿的效果
//越多越好? 消耗内存和cpu的使用效率
//创建多线程的方式
//1.NSThread
//2.CGD
//3.NSOperation
//程序运行的时候,会默认创建一个线程,这个线程称之为主线程/UI线程,所有的UI相关的操作都需要在这个线程中执行
BOOL res1 = [NSThread isMainThread]; //判断是否是子线程
BOOL RES2 = [NSThread isMultiThreaded]; //判断是否是多线程
//1.创建线程 减方法创建
//操作 – 功能 – 若干行代码 – 函数 – 任务
NSThread * thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil];
//第三个参数: object 如果第二个参数selector带参的话 就使用object传递
//新创建的线程称之为子线程或者任务线程/后台线程
//线程的名字
thread1.name = @"子线程1";
//线程的优先级(double) 0 - 1
thread1.threadPriority = 0.5;
//设置线程的优先级(枚举值)需要在线程开启前设置 否则无效。
thread1.qualityOfService = NSQualityOfServiceBackground;
//2.开启线程
[thread1 start];
//创建线程 加方法创建并且执行线程
[NSThread detachNewThreadSelector:@selector(threadRun) toTarget:self withObject:nil];
//设置不了线程的名字 可以到方法里通过currentThread方法拿到线程 再进行赋值操作
//NSObject方法 创建线程
[self performSelectorInBackground:@selector(threadRun) withObject:nil];
GCD
//GCD - 基于C语言的一套API接口,它是把Block作为任务,添加到队列中进行操作
//GCD 优点:可以控制多个线程的执行顺序 这个功能是NSThread不具有的
//缺点: 不能取消线程
//GCD 语法都是c语言函数
//1.创建队列
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
//queue 队列/线程池
//dispatch 调度/执行
//第一个参数 队列的名字 通常写nil
//第二个参数 串行/并行
//DISPATCH_QUEUE_SERIAL 串行 按顺序执行队列里的线程
//DISPATCH_QUEUE_CONCURRENT 并行 同时执行若干个线程
监听线程的退出
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadOut) name:NSThreadWillExitNotification object:nil];
//NSThreadWillExitNotification 线程退出的频道
//只需要监听这个频道 当线程退出的时候 就会收到通知/消息 然后调用响应的方法
//取消线程 cancel并不能把一个线程退出 调用cancle方法只是为了告诉当前线程的isCancelled属性为YES 并且触发监听
[thread cancel];
if (thread.isCancelled) {
//如果为YES 退出线程 也可以使用return
[NSThread exit];
}
//threadRun这个方法是我们子线程的入口函数 - 当这个方法执行完毕的时候,线程就已经是死亡状态了
定时器
//GCD定时器
//1.创建定时器
//需要把定时器定义成全局变量 否则会被立刻释放
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
//2.设置定时器 第一个参数为时间间隔 第二个参数次数
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//3.定时器定时的操作
dispatch_source_set_event_handler(self.timer, ^{
//定时器每隔几秒钟要做的操作
NSLog(@"d");
});
//执行定时器
dispatch_resume(self.timer);
延时操作
[NSThread sleepForTimeInterval:2.0];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
-(void)createQueue2{
NSLog(@"将要延时...");
//延时几秒操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//DISPATCH_TIME_NOW 从现在开始
//NSEC_PER_SEC 代表的是秒
//延时几秒之后的操作
NSLog(@"延时执行");
});
[self performSelector:@selector(afterRun) withObject:nil afterDelay:2.0];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
线程组
//线程组 监听线程
dispatch_group_t group = dispatch_group_create();
//创建全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"线程1执行完毕");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:3.0];
NSLog(@"线程2执行完毕");
});
//前两个线程结束后会通知这个线程
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"返回主线程");
});
阻塞线程组
dispatch_group_t group = dispatch_group_create();
//异步操作组
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 5; i ++) {
//进入线程组
dispatch_group_enter(group);
[NSThread sleepForTimeInterval:1.f];
NSLog(@"线程 --- %d",i);
//离开线程组
dispatch_group_leave(group);
}
});
//目的:先执行完前面的线程 再执行后面的线程
//阻塞线程组
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//异步操作队列
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"另一个线程执行了");
});
pragma mark - 重复
-(void)createQueue1{
//重复执行某个线程
//第一个参数 重复执行的次数
dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t idx) {
NSLog(@”线程执行…”);
});
}
pragma mark - 栅栏
-(void)createQueue2{
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue, ^{
NSLog(@"线程1");
});
dispatch_async(queue, ^{
NSLog(@"线程2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"barrier...");
});
dispatch_async(queue, ^{
NSLog(@"线程3");
});
}
pragma mark - 信号量
-(void)createQueue3{
//信号量
//创建信号量 参数为计数
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//等待接收信号 接收信号要写在当前线程所有操作之前 计数-1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"线程1执行...");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"线程2执行...");
//发送信号 在当前线程操作执行完毕之后 再发送信号 计数+1
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"线程3执行...");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"线程4执行...");
});
}
以上是关于iOS开发 - 多线程的主要内容,如果未能解决你的问题,请参考以下文章