进程
进程是指在系统中正在运行的一个应用程序
每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内
线程
一个进程要想执行任务,必须得有线程(每一个进程至少要有一条线程)
一个进程(程序)的所有任务都在线程中执行
多线程原理
同一时间,CPU只能处理一条线程,只有一条线程在执行
多线程并发(同时)执行,其实就是CPU快速的在多条线程之间调度(切换)
如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
多线程优点
能适当提高程序的执行效率
能适当提高资源利用率(CPU、内存利用率)
多线程的缺点
创建线程是有开销的,iOS下主要成本包括:内核数据结构(大约 1 KB)、栈空间(子线程 512 KB、主线程 1 MB, 也可以使用 setStackSize: 设置,但必须是 4 K 的倍数,而且最小是 16 K),创建线程大约需要90毫秒的创建时间
线程越多,CPU在调度线程上的开销就越大
程序设计更加复杂: 比如线程之间的通信、多线程的数据共享
如果开启大量的线程,会降低程序的性能:
CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源
每条线程被调度执行的频次会降低(线程的执行效率降低)
移动开发中会导致设备发烫,耗电量加大
主线程(UI线程)的主要作用
显示/刷新 UI界面
处理UI事件(比如点击事件、滚动事件、拖拽事件等)

Pthread
/*
pthread_create(pthread_t _Nullable *restrict _Nonnull,
const pthread_attr_t *restrict _Nullable,
void * _Nullable (* _Nonnull)(void * _Nullable),
void *restrict _Nullable)
参数:
1.线程代号的地址
2.线程的属性
3.调用函数的指针
4.传递给函数的参数
返回值:
- 如果是 0, 表示正确
- 如果是非0,表示错误码
第三个参数说明:
void *--(*)--(void *)
返回值--(函数指针)--(参数)
void * 相当于 OC中的id
*/
#import <pthread.h> //POSIX 多线程开发框架
- (void)pthreadDemo{
pthread_t threadID;
NSString * str = @"Hello Pthread";
/*
- 在 ARC 开发中, 如果遇到 OC和C 语言中相同数据类型进行转换,需要使用__bridge "桥接"
- 在 MRC 开发中,不需要桥接
- 在 OC 中,如果是ARC开发,编译器会在编译的时候,自动根据代码结构,添加 retain , release, autorelease
- ARC 只负责 OC部分的代码,不负责C的代码. 如果C的代码里面出现了 retain/create/copy 字样的函数,都需要release
*/
int result = pthread_create(&threadID, NULL, &demo, (__bridge void *)(str));
if (result == 0) {
for (int i = 0; i < 20; i++) {
NSLog(@"OK!");
}
}else{
NSLog(@"error %d",result);
}
void * demo(void *param){
NSString * sss = (__bridge NSString *)(param);
NSLog(@"%@---%@",[NSThread currentThread],sss);
return NULL;
}
NSThread
创建方式:
一个NSThread对象就代表一条线程
线程一启动,就会在线程thread中执行self的run方法
//创建线程
NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(demo:) object:@"thread"];
//启动线程
[thread start];
//二、创建线程后自动启动线程
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"Detach"];
//三、隐式创建并启动线程,非常方便.不用NSThread对象
//NSObject的分类 意味着所有的基础NSObject的都可以使用这个方法
[self performSelectorInBackground:@selector(demo:) withObject:@"Background"];
上述2种创建方式优缺点
优点:简单快捷
缺点:无法对线程进行更详细的设置
线程状态
// 启动线程,进入就绪状态
- (void)start;
//阻塞(暂停)线程,进入阻塞状态
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//强制停止线程 进入死亡状态
//一旦强行终止线程,后续的所有代码都不会被执行
//注意:在终止线程执行前,应该要释放之前分配的对象!!
+ (void)exit;
//注意一旦线程停止(死亡)了,就不能再次开启任务
线程属性
NSThread * t = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
//在大型的商业项目中,通常希望程序在崩溃的时候,能够获取到程序所在的线程!
t.name = @"Thread A";
//优先级 从 0.0 -- 1.0 默认值 0.5
/** 优先级翻转
优先级 只是保证 CPU 调度的可能性会高!
多线程目的:将耗时操作放在后台,不阻塞UI线程!
建议:在开发的时候,不要修改优先级
在多线程开发中,不要相信一次的运行结果!!
*/
t.threadPriority = 0.1;
[t start];
线程安全
//nonatomic : 非原子属性,不会为setter方法加锁
//atomic : 原子属性,为setter方法加锁(默认)单写多读的一种多线程技术
//@synchronized
//互斥锁 的范围 应该尽量小,范围大了 效率就差
//参数:任意OC对象都OK!一般用self!全局对象
//局部变量是每一个线程单独拥有的,因此没法加锁!!!
/**
实际上,原子属性内部有一个锁,自旋锁
自旋锁 & 互斥锁
- 共同点:
都能够保证线程安全.
- 不同点:
互斥锁:如果线程被锁在外面,哥么就会进入休眠状态,等待锁打开,然后被唤醒!
自旋锁:如果线程被锁在外面,哥么就会用死循环的方式,一直等待锁打开!
无论什么锁,都很消耗新能.效率不高
* 线程安全
在多个线程进行读写操作时,保证数据正确!
* UI 线程,共同约定:所有更新UI 的操作都放在主线程上执行!
原因:UIKit框架都是线程不安全的!!(因为线程安全效率下降!)
*/


NSData * data ;
//原子属性 == YES 先把文件保存在一个临时的文件中,等全部写入之后,再改名
[data writeToFile:@"hank.mp4" atomically:YES];
线程间通讯
//五种方法:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array ;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;
网友评论