定义
线程
- 进程的基本执行单元,一个进程的所有任务都在线程中执行
- 进程想要执行任务,必须得有线程,进程至少要有一条线程
- 程序启动会默认开启一条线程,这条线程被称为主线程或UI线程
-
进程
系统中正在运行的一个应用程序
- 每个进程之前是独立的,每个进程均运行在其专用的且受保护的内存空间内
-
说明
iOS是单进程,Android是多进程,可以进程间通讯,多任务是多线程。
- 进程是有内存空间的,线程没有内存空间,共享使用本进程的空间,而进程之前则是独立的地址空间
- TLS(Thread Local Storage),线程局部存储(例:同步锁时,存储syncData),是某些操作系统为线程单独提供的私有空间,但通常只具有很有限的容量。
iOS中有pthread、NSThread、NSOperation、GCD
原理
-
状态
新建线程,调用start后,线程进入就绪状态(runnable),此时cpu可调度,当cpu调度当前线程,状态为运行(running),当前线程有两种状态
- 调用sleep、等待同步锁、从可调度线程池移除,进入阻塞状态(Blocked)
- 直接挂掉(Dead)
可调度线程池
- 线程池存在阈值(任何任务都是依赖于线程执行)
- 核心线程是否正在运行,是否都有任务,创建新的工作线程去执行
- 线程池工作队列是否饱和->将任务存储在工作队列
- 线程池中的线程都处于执行状态->安排线程去执行
- 交给饱和策略去处理(这四种拒绝策略均实现了RejectedExecutionHandler接口)
- 直接抛出异常RejectExecutionExeception异常来阻止系统正常运行
- 将任务回退到调用者
- 丢掉等待最久的任务
- 直接丢掉任务
- 线程池存在阈值(任何任务都是依赖于线程执行)
- 任务的执行速度的影响因素
- cpu调度
- 任务复杂度
- 线程状态
- 优先级
- 优先级反转
- IO密集型 -> 频繁等待,IO相对于cpu优先级提升
- CPU密集型 -> 很少等待
- 饿死,OSSpinLock
- 调度,任务的优先级不断提升,造成优先级反转
优先级因素
自旋锁,发现其它线程执行当前线程,询问+忙等,耗费性能比较高。
短小任务使用,比如atomic。
- atomic是原子属性,是为多线程开发准备的,是默认属性
- 仅仅在属性的Setter方法中,增加了锁(自旋锁),能够保证同一时间,只有一条线程对属性进行‘写’操作,同一时间 单线程写多线程读的线程处理技术
- atomic在底层源码中只是一个标识符判断,判断是否添加自旋锁,底层是spinLock
- noatomic,没有锁,性能高 ```cpp static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) { ….
if (!atomic) { oldValue = slot; slot = newValue; } else { spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); oldValue = slot; slot = newValue;
slotlock.unlock(); }objc_release(oldValue); } ```
互斥锁,发现其它线程执行当前线程,休眠(就绪状态)一直在等打开,唤醒执行。
GCD
将任务添加到队列,并指定执行任务的函数
- 以下任务block并未执行block(),添加到了队列后,指定了函数执行 ```cpp
(void)syncTest { ///<< 创建任务 dispatch_block_t block = ^{
NSLog(@"hello, GCD");
}; ///<< 添加到队列 dispatch_queue_t queue = dispatch_queue_create(“com.cc.cn”, NULL); ///<< 指定函数执行 dispatch_async(queue, block); } ```
队列
队列,是一种数据结构,FIFO
- 串行队列,任务1一定在任务2前,一个个执行
- 并发队列,具备任务调度能力,但不具备执行能力,执行依赖线程池中的线程
- 无论同步函数还是异步函数,都是耗时任务
```cpp
// 同步队列
dispatch_queue_t queue = dispatch_queue_create(“cooci”, DISPATCH_QUEUE_CONCURRENT);
NSLog(@”1”);
// 异步函数
dispatch_async(queue, ^{
}); NSLog(@”5”);NSLog(@"2");
// 同步
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
运行结果 1 -> 5 -> 2 -> 3 -> 4
- **以下会出现死锁,打印 1 -> 5 -> 2,无论去掉12行与否,都会死锁,任务块在后面添加新任务**
- **_dispatch_sync_f_slow**
```cpp
// 同步队列
dispatch_queue_t queue = dispatch_queue_create("cooci", NULL);
NSLog(@"1");
// 异步函数
dispatch_async(queue, ^{
NSLog(@"2");
// 同步函数:护犊子,堵塞11行以后的代码执行
dispatch_sync(queue, ^{
NSLog(@"3");
});
// 无论去掉以下代码都会死锁
NSLog(@"4");
});
NSLog(@"5");
补充
- 以下函数可能打印结果:
- 任务依次加入到主队列中,加到NSLog(0)后,但是任务3护犊子,所以先执行
- queue为并发队列时
- 任务3是同步函数,会阻塞其后面的执行,所以任务3在任务4前执行
- 任务4一定在任务5、6、7前执行
- 任务1、2、3顺序不固定
- 任务7、8、9顺序不固定
- 答案是A、C
- queue为串行队列时
- 自上而下依次执行dispatch_async -> dispatch_async -> dispatch_sync -> …
- 任务NSLog被加入到串行队列中,所以打印顺序为1230789
- 答案是A
dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_CONCURRENT);
///<< 任务1
dispatch_async(queue, ^{
NSLog(@"1");
});
///<< 任务2
dispatch_async(queue, ^{
NSLog(@"2");
});
///<< 任务3
dispatch_sync(queue, ^{ NSLog(@"3"); });
///<< 任务4
NSLog(@"0");
///<< 任务5
dispatch_async(queue, ^{
NSLog(@"7");
});
///<< 任务6
dispatch_async(queue, ^{
NSLog(@"8");
});
///<< 任务7
dispatch_async(queue, ^{
NSLog(@"9");
});
// A: 1230789
// B: 1237890
// C: 3120798
// D: 2137890