美文网首页
RunLoop相关知识

RunLoop相关知识

作者: 浪淘沙008 | 来源:发表于2019-12-04 09:49 被阅读0次

    如何实现一个多线程

    • 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;
    }
    
    

    相关文章

      网友评论

          本文标题:RunLoop相关知识

          本文链接:https://www.haomeiwen.com/subject/ovskgctx.html