基本概念:
1.进程:mac系统中运行的每一个应用程序就是一个进程,进程之间是独立的,有自己的内存区域,可在活动管理器中查看
2.线程:每个进程最少有一条线程,可有多条线程,任务都在线程中执行
3.线程串行:如果在一个线程中执行多个任务,只能排队顺序执行,一个完了执行下一个
4.线程并行(多线程):一个进程开启N条线程,每条线程执行不同任务,可提高任务效率
5.cpu处理:同一时间,cpu只能处理一条线程,处理多条线程只是在不同线程间快速切换处理,只因为足够快速,给人的感觉就像同时处理;线程开启的越多,耗费cpu资源越多,线程开启过多,导致cpu利用率过高,执行效率降低,通俗表现就是卡、慢
6.优缺点:iOS下创建线程的开销包括:内核数据结构(大约1KB)、栈空间(子线程512k,主线程1M,也可以使用-setStackSize:设置,但必须是4K的倍数,最小为16K),创建线程大约为90ms
1⃣️优点是执行效率高,提高资源利用率;2⃣️缺点是开启过多时,会降低性能,开销变大,业务逻辑也会复杂,比如线程间通信
7.每个进程会默认启动一条线程,称为主线程或UI线程,主要用来显示刷新UI及处理UI事件,如果主线程操作比较耗时,会造成主线程卡顿,耗时操作建议放置于子线程进行,
8.多线程常用技术:NSThread、GCD、NSOperation
………………………………………………………………………………………………………………………………………………………………
NSThread方案:
一个NSThread对象就代表一条线程
1. 创建、启动线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
1.1 主线程相关用法
+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程
1.2 获得当前线程
NSThread *current = [NSThread currentThread];
1.3 线程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
2. 创建子线程(快速创建新线程)
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; //Object:传参数进去
3. 隐式创建子线程
[self performSelectorInBackground:@selector(run) withObject:nil]; //Object:传参数进去
上述2种创建线程方式简单快捷,无法对线程进行更详细的设置名字、优先级等
4. 线程状态:
5.控制线程状态
5.1启动线程
- (void)start; //进入就绪状态 》运行状态,任务执行完毕,进入死亡状态
5.2阻塞线程
+ (void)sleepUntilDate:(NSDate *)date; //[NSDate dateWithTimeIntervalSinceNow:2]从现在开始阻塞2秒
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
5.3强制停止线程
+ (void)exit;//线程停止,就不能再次开启
6.线程安全问题
一块资源被多条线程访问,可能会引起数据错乱和数据安全问题
6.1 安全解决方案 -- 互斥锁 --(线程同步)
格式:@synchronized(锁对象){需要锁定的代码}
加锁后,锁定的代码只会被某一条线程所访问。锁对象需要是一个全局对象,多个锁对象无效。一般使用self就行。比较消耗资源
7.原子和非原子属性
atomic:原子属性,为setter方法加锁,系统默认,线程安全,耗费资源
nonatomia:非原子属性,不为setter方法加锁,非线程安全,适合移动设备,默认使用
8.线程间通信
以下载图片为例,图片在子线程下载,完成后需要在主线程加载刷新UI,这就是简单的线程间通信
8.1线程间通信常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
举个栗子:
// 回到主线程,显示图片
[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
- (void)showImage:(UIImage *)image
{
self.imageView.image = image;
}
等价
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
//意思是在主线程中调用setter方法,将数据设置给imageview,waitUntilDone:NO--设置为NO意味着调用完setter方法,就继续执行下面的代码,不等待,YES则是完成后,才继续执行下面的代码
9.计算图片下载使用时长
9.1 使用当前日期时间
NSDate *begin = [NSDate date];
// 根据图片的网络路径去下载图片数据
NSData *data = [NSData dataWithContentsOfURL:url];
NSDate *end = [NSDate date];
NSLog(@"%f", [end timeIntervalSinceDate:begin]);
9.2 使用自1970以来的秒数计算
CFTimeInterval begin = CFAbsoluteTimeGetCurrent();
// 根据图片的网络路径去下载图片数据
NSData *data = [NSData dataWithContentsOfURL:url];
CFTimeInterval end = CFAbsoluteTimeGetCurrent();
NSLog(@"%f", end - begin);