线程和进程的定义:
线程:
- 线程时进程的
基本执行单元
,一个进程的所有任务都在线程中执行 - 进程要想执行任务,必须的有线程,进程
至少
要有一条线程 - 程序启动会
默认
开启一条线程,这条线程被称为主线程
或者UI线程
进程:
- 进程是指在系统中正在运行的一个
应用程序
- 每个进程之间是
独立
的,每个进程均运行在其专用的且受保护的内存空间内。
关联和区别:
- 不同线程之间
共享
同一个进程的资源空间 - 不同进程之间是
独立
的 - 进程要比线程要
健壮
,一个线程死了,同一进程之间的线程也都挂了,整个进程就挂了;进程挂了,不影响其他的进程 -
线程
是处理器调度的基本单位
,但是进程不是。 - 线程
没有地址空间
,线程包含
在进程地址空间中
线程与runloop的关系:
- 线程与runloop是
一一
对应的,每一条子线程都会对应着一个runloop - runloop是用来
管理
线程的,当线程的runloop被开启时,线程在执行完任务之后会休眠期,直到有新的任务才被唤醒。 - :runloop在
第一次获取
时被创建,在线程结束
时被销毁。 - 对于子线程来说,runloop是
懒加载
的,只有当我们使用的时候才会创建,所以在子线程用定时器
要注意确保子线程的runloop被创建,不然定时器不会回调。
多线程的原理:
- 对于
单核CPU
来说,多线程的效果实际是一种假象
,因为同一时间
只能处理一条线程
,只是因为cpu的调度足够快
,在多个线程中来回地切换执行,才有的多线程一说
优点:
- 能适当提高程序的执行效率
- 能适当提高资源的利用率,如CPU、内存
- 线程上的任务执行完成后,线程会自动销毁
缺点:
- 开启线程需要占用一定的内存空间,默认情况下,每一个线程占用512KB
- 如果开启大量线程,会占用大量的内存空间,降低程序的性能
- 线程越多,CPU在调用线程上的开销就越大
- 程序设计更加复杂,比如线程间的通信,多线程的数据共享
线程的生命周期:
新建(创建)
--> 就绪(可以执行了,等待CPU进行调度)
-->运行(开始执行任务)
--> 阻塞(锁、其他条件)
--> 死亡(结束)
多线程的四种使用方式
PThread
- 面向C、适用于不同系统
NSThred
- 面向对象,可直接操作线程对象
GCD
-多核、方便、操作简单
NSOpreation
- 基于GCD,面向对象
// *********1: pthread*********
pthread_t threadId = NULL;
//c字符串
char *cString = "HelloCode";
/**
pthread_create 创建线程
参数:
1. pthread_t:要创建线程的结构体指针,通常开发的时候,如果遇到 C 语言的结构体,类型后缀 `_t / Ref` 结尾
同时不需要 `*`
2. 线程的属性,nil(空对象 - OC 使用的) / NULL(空地址,0 C 使用的)
3. 线程要执行的`函数地址`
void *: 返回类型,表示指向任意对象的指针,和 OC 中的 id 类似
(*): 函数名
(void *): 参数类型,void *
4. 传递给第三个参数(函数)的`参数`
*/
int result = pthread_create(&threadId, NULL, pthreadTest, cString);
if (result == 0) {
NSLog(@"成功");
} else {
NSLog(@"失败");
}
//*********2、NSThread*********
[NSThread detachNewThreadSelector:@selector(threadTest) toTarget:self withObject:nil];
//*********3、GCD*********
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self threadTest];
});
//*********4、NSOperation*********
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
[self threadTest];
}];
- (void)threadTest{
NSLog(@"begin");
NSInteger count = 1000 * 100;
for (NSInteger i = 0; i < count; i++) {
// 栈区
NSInteger num = i;
// 常量区
NSString *name = @"zhang";
// 堆区
NSString *myName = [NSString stringWithFormat:@"%@ - %zd", name, num];
NSLog(@"%@", myName);
}
NSLog(@"over");
}
void *pthreadTest(void *para){
// 接 C 语言的字符串
// NSLog(@"===> %@ %s", [NSThread currentThread], para);
// __bridge 将 C 语言的类型桥接到 OC 的类型
NSString *name = (__bridge NSString *)(para);
NSLog(@"===>%@ %@", [NSThread currentThread], name);
return NULL;
}
线程安全问题:
互斥锁:
- 在同一时间,保证了只有一条线程执行任务,即保证了相应同步的功能
- 发现其他线程执行,当前线程 休眠(即就绪状态),进入
等待执行
即挂起
。一直等其他线程打开
之后,然后唤醒
执行
自旋锁:
在同一时间,保证了只有一条线程执行任务,即保证了相应同步的功能
发现其他线程执行,当前线程 一直询问(即一直访问),处于
忙等
状态,耗费的性能比较高
原子锁atomic:
原子属性(线程安全),针对多线程设计的,
默认值
保证同一时间只有
一个
线程能够写入(但是同一个时间多个线程都可以取值)atomic 本身就有一把锁(
自旋锁
)单写多读
:单个线程写入,多个线程可以读取线程安全
,需要消耗大量的资源
非原子nonatomic:
非原子属性
非线程安全,适合内存小的移动设备
线程间的通信:
performSelector
条件
.....