RunLoop相关知识
2019-12-04 本文已影响0人
浪淘沙008
如何实现一个多线程
- runloop是通过内部维护的事件循环来对事件/消息进行管理的一个对象
- 事件循环
- 没有消息需要处理时进行休眠防止资源占用,即使线程从用户态转化为内核态
- 有消息需要处理时立刻被唤醒,即使线程从内核态转化为用户态
- 事件循环
- 在main函数中会调用一个UIApplication函数,该函数中会启动一个主线程的runloop来维持主线程状态的切换
CFRunloop结构
- CFRunLoop
struct __CFRunLoop {
pthread_t _pthread; //runloop和线程是一一对应的关系,runloop可以脱离线程独自存在
CFMutableSetRef _commonModes; //NSMutableSet<NSString *> 字符串类型的集合
CFMutableSetRef _commonModeItems; //集合,其中包含Observer/timer/source
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes; //NSMutableSet<CFRunLoopMode *>多个mode的集合
};
- CFRunLoopMode
struct __CFRunLoopMode {
CFStringRef _name; //字符串描述mode类型,当添加到mode中时通过字符串对应的名称进行添加
CFMutableSetRef _sources0; //MutableSet 无序
CFMutableSetRef _sources1; //MutableSet
CFMutableArrayRef _observers; //MutableArray 有序
CFMutableArrayRef _timers; //MutableArray
}
- sources0
- 需要手动唤醒线程,
- sources1
- 有自动唤醒线程的能力
- CFRunLoopTimer
- 基于事件的定时器,与NSTimer基于toll-free bridged互相转换
- CGRunLoopObserver
- 观察点
- kCFRunLoopEntry 将要进入
- kCFRunLoopBeforeTimers 将要对timer事件进行处理
- kCFRunLoopBeforeSources 将要处理sources事件
- kCFRunLoopBeforeWaiting 即将进入睡眠
- kCFRunLoopAfterWaiting 被唤醒
- kCFRunLoopExit 退出
- kCFRunLoopAllActivities 所有的状态
- 观察点
//监测runloop的状态,
-(void)observer
{
//1.创建监听者
/*
第一个参数:怎么分配存储空间
第二个参数:要监听的状态 kCFRunLoopAllActivities 所有的状态
第三个参数:时候持续监听
第四个参数:优先级 总是传0
第五个参数:当状态改变时候的回调
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
/*
kCFRunLoopEntry = (1UL << 0), 即将进入runloop
kCFRunLoopBeforeTimers = (1UL << 1), 即将处理timer事件
kCFRunLoopBeforeSources = (1UL << 2),即将处理source事件
kCFRunLoopBeforeWaiting = (1UL << 5),即将进入睡眠
kCFRunLoopAfterWaiting = (1UL << 6), 被唤醒
kCFRunLoopExit = (1UL << 7), runloop退出
kCFRunLoopAllActivities = 0x0FFFFFFFU
*/
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"即将进入runloop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理timer事件");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理source事件");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将进入睡眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"被唤醒");
break;
case kCFRunLoopExit:
NSLog(@"runloop退出");
break;
default:
break;
}
});
/*
第一个参数:要监听哪个runloop
第二个参数:观察者
第三个参数:运行模式
*/
CFRunLoopAddObserver(CFRunLoopGetCurrent(),observer, kCFRunLoopDefaultMode);
//NSDefaultRunLoopMode == kCFRunLoopDefaultMode
//NSRunLoopCommonModes == kCFRunLoopCommonModes
}
- RunLoop有多个mode的原因
- 不同的mode对应不同的状态,便于进行事件的屏蔽,起到事件隔离的作用
FPS监测实现
- 方法一:通过监听RunLoop循环实现监测
#import "SMLagMonitor.h"
@interface SMLagMonitor() {
int timeoutCount;
CFRunLoopObserverRef runLoopObserver;
@public
dispatch_semaphore_t dispatchSemaphore;
CFRunLoopActivity runLoopActivity;
}
@property (nonatomic, strong) NSTimer *cpuMonitorTimer;
@end
@implementation SMLagMonitor
#pragma mark - Interface
+ (instancetype)shareInstance {
static id instance = nil;
static dispatch_once_t dispatchOnce;
dispatch_once(&dispatchOnce, ^{
instance = [[self alloc] init];
});
return instance;
}
- (void)beginMonitor {
//判断observer是否已经存在,如果存在则处于启动状态直接返回
if (runLoopObserver) {
return;
}
dispatchSemaphore = dispatch_semaphore_create(0); //Dispatch Semaphore保证同步
//创建一个观察者
CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};
runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,
kCFRunLoopAllActivities,
YES,
0,
&runLoopObserverCallBack,
&context);
//将观察者添加到主线程runloop的common模式下的观察中
CFRunLoopAddObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);
//创建子线程监控
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"开启子线程监测");
//子线程开启一个持续的loop用来进行监控
while (YES) {
// 通过while和dispatch_semaphore_wait实现周期性的循环扫描
long semaphoreWait = dispatch_semaphore_wait(dispatchSemaphore, dispatch_time(DISPATCH_TIME_NOW, 20*NSEC_PER_MSEC));
if (semaphoreWait != 0) {
// 判断observer是否存在,如果不存在则处于停止监控状态,重置数据后返回
if (!runLoopObserver) {
timeoutCount = 0;
dispatchSemaphore = 0;
runLoopActivity = 0;
return;
}
//两个runloop的状态,BeforeSources和AfterWaiting这两个状态区间时间能够检测到是否卡顿
if (runLoopActivity == kCFRunLoopBeforeSources || runLoopActivity == kCFRunLoopAfterWaiting) {
//出现三次出结果
// if (++timeoutCount < 3) {
// continue;
// }
NSLog(@"monitor trigger");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// 出现卡顿状态可进行后面的u业务处理
});
} //end activity
}// end semaphore wait
timeoutCount = 0;
}// end while
});
}
// 停止监控的方法
- (void)endMonitor {
[self.cpuMonitorTimer invalidate];
if (!runLoopObserver) {
return;
}
CFRunLoopRemoveObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);
CFRelease(runLoopObserver);
runLoopObserver = NULL;
}
#pragma mark - Private
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
SMLagMonitor *lagMonitor = (__bridge SMLagMonitor*)info;
lagMonitor->runLoopActivity = activity;
dispatch_semaphore_t semaphore = lagMonitor->dispatchSemaphore;
dispatch_semaphore_signal(semaphore);
}
@end
- 方法二:通过CADisplayLink进行监测,该方法通过CADisplayLink的频率和界面刷新频率的一致性来监测界面卡顿
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
- (void)tick:(CADisplayLink *)link {
if (_lastTime == 0) {
_lastTime = link.timestamp;
return;
}
_count++;
NSTimeInterval delta = link.timestamp - _lastTime;
if (delta < 1) return;
_lastTime = link.timestamp;
float fps = _count / delta;
NSLog(@"帧率%f", fps);
_count = 0;
}