Runloop
2016-05-21 09:01
441 查看
一.我们先来介绍runloop的基础知识
1. 什么是runloop ?
字面意思理解就是,运行循环。但是,要说的话,我认为是这样的,每一个线程都有一个一一对应的runloop,只有主线程的runloop是自动创建的,子线程runloop是不会自动创建的,需要的时候,需要我们自己去创建,那么问题来了,什么时候去创建runloop,官方文档明确写出,runloop在第一次获取的时候创建,就是说除非我们主动的创建他,否则在子线程不会存在runloop。那么这里就引出一个问题,为什么子线程不可以像主线程一样自动创建runloop,这里我们就需要弄清楚一个问题,runloop是干什么的? runloop实际上是一个对象,这个对象管理了其需要处理的事件和消息 并且提供提供了一个入口函数执行上面的eventloop的逻辑,线程执行了这个函数之后就会一直处于这个函数的内部 “接受消息->等待->处理”的循环中,知道这个循环结束(比如传入quit消息),函数返回。用我们的白话来说的话,runloop就是用来管理线程的,有了他的管理可以节省我们cup资源,但是这个只是针对,我们一直的使用的线程,比如主线程,对于其它子线程如果没有什么必要,不必须去创建一个runloop,即使线程处于睡眠还是会占用一定的系统资源。
源码可以看出,runloop存放在dictionary key是pthread_t,value是CFRunloop,获取一个线程对应的runloop是对应的流程是1.如果是第一次进入,初始化全局dic,并为主线程创建一个runloop。
2.直接从dictionary里获取,如果取不到,去创建一个。
3.注册一个回调,当线程销毁时,顺便也销毁其对应的runloop。
1. 什么是runloop ?
字面意思理解就是,运行循环。但是,要说的话,我认为是这样的,每一个线程都有一个一一对应的runloop,只有主线程的runloop是自动创建的,子线程runloop是不会自动创建的,需要的时候,需要我们自己去创建,那么问题来了,什么时候去创建runloop,官方文档明确写出,runloop在第一次获取的时候创建,就是说除非我们主动的创建他,否则在子线程不会存在runloop。那么这里就引出一个问题,为什么子线程不可以像主线程一样自动创建runloop,这里我们就需要弄清楚一个问题,runloop是干什么的? runloop实际上是一个对象,这个对象管理了其需要处理的事件和消息 并且提供提供了一个入口函数执行上面的eventloop的逻辑,线程执行了这个函数之后就会一直处于这个函数的内部 “接受消息->等待->处理”的循环中,知道这个循环结束(比如传入quit消息),函数返回。用我们的白话来说的话,runloop就是用来管理线程的,有了他的管理可以节省我们cup资源,但是这个只是针对,我们一直的使用的线程,比如主线程,对于其它子线程如果没有什么必要,不必须去创建一个runloop,即使线程处于睡眠还是会占用一定的系统资源。
/// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef static CFMutableDictionaryRef loopsDic; /// 访问 loopsDic 时的锁 static CFSpinLock_t loopsLock; /// 获取一个 pthread 对应的 RunLoop。 CFRunLoopRef _CFRunLoopGet(pthread_t thread) { OSSpinLockLock(&loopsLock); if (!loopsDic) { // 第一次进入时,初始化全局Dic,并先为主线程创建一个 RunLoop。 loopsDic = CFDictionaryCreateMutable(); CFRunLoopRef mainLoop = _CFRunLoopCreate(); CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop); } /// 直接从 Dictionary 里获取。 CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread)); if (!loop) { /// 取不到时,创建一个 loop = _CFRunLoopCreate(); CFDictionarySetValue(loopsDic, thread, loop); /// 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop。 _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop); } OSSpinLockUnLock(&loopsLock); return loop; } CFRunLoopRef CFRunLoopGetMain() { return _CFRunLoopGet(pthread_main_thread_np()); } CFRunLoopRef CFRunLoopGetCurrent() { return _CFRunLoopGet(pthread_self()); }
源码可以看出,runloop存放在dictionary key是pthread_t,value是CFRunloop,获取一个线程对应的runloop是对应的流程是1.如果是第一次进入,初始化全局dic,并为主线程创建一个runloop。
2.直接从dictionary里获取,如果取不到,去创建一个。
3.注册一个回调,当线程销毁时,顺便也销毁其对应的runloop。
讲到这里有必要谈深入一下runloop内部的结构
每一个runloop对应一个thread,他们是一一对应的关系。每一个CFRunloop对应多个 CFRunLoopMode,一个CFRunloop又对应多个,CFRunloop,CFRunloopTimer,CFRunLoopObserver 每一次调用runloop主函数时,都会指定一个mode这个mode称作currentmode,如果要切换mode只能退出runloop,为什么要这么做呢?因为为了分隔开不同组的 Source/Timer/Observer,让其互不影响。
RunLoop 的 Mode CFRunLoopMode和CFRunloop 的结构大致如下: struct __CFRunLoopMode { CFStringRef _name; // Mode Name, 例如 @"kCFRunLoopDefaultMode" CFMutableSetRef _sources0; // Set CFMutableSetRef _sources1; // Set CFMutableArrayRef _observers; // Array CFMutableArrayRef _timers; // Array ... }; struct __CFRunLoop { CFMutableSetRef _commonModes; // Set CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer> CFRunLoopModeRef _currentMode; // Current Runloop Mode CFMutableSetRef _modes; // Set ... }; 这里有个概念叫做"commonmodes":一个mode可以将自己标记为"common"属性(通过将其modename添加到runloop的commonmodes中)。每当runloop的内容容易发生变化时,runloop都会自动将_commonModelIteams里的source/observer/timer,同步到具有 "common"标记的所有mode里。应用场景举例,主线程的runloop里有两个预设置的mode:KCFRunLoopDefaultMode和UITrackingRunLoopMode。这两个mode都已经被标记为"common"属性。defaultmode是app平时所处的状态,trackingrunloopmode是追踪scrollview滑动的是状态。当你创建一个timer并加到,defaultmode时,timer会得到重复的回调,但此时,滑动一个tableview时,runloop会将mode切换为trackingrunloopmode,这时,timer就不会被回调,并且也不会影响到滑动的操作。
CFRunLoopSourceRef 是事件产生的地方。source有两个版本:source0和source1。source0 只包含了一个回调(函数指针)它并不能主动触发事件。使用时,你需要先调用CFRunloopSourceSignal(source),将这个source标记为待处理,然后,手动调用CFRunLoopWakeUp(runloop)来唤醒runloop,让其处理这个事件,source1包含了一个match_port和一个回调(函数指针),被用于通过内核和其它线程相互发送消息。这种source能主动唤醒runloop的线程,其原理下面会讲到。 CFRunLoopTimerRef 是基于时间的触发器,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。 CFRunLoopObserverRef 是观察者,每个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。 Source/Timer/Observer 被统称为 mode item,一个 item 可以被同时加入多个 mode。但一个 item 被重复加入同一个 mode 时是不会有效果的。如果一个 mode 中一个 item 都没有,则 RunLoop 会直接退出,不进入循环。
相关文章推荐
- Linux实践二:模块
- linux 系统时间硬件时间同步
- PHOTOSHOP CC 2015插件安装方法
- 网站分析与优化
- vim 编辑器 bash文件测试
- codeforces611B. New Year and Old Property
- Zabbix 使用 JMX 方式监控 Tomcat
- centosx64位寄存器
- linux下安装jdk
- centos x86_64--------------------------------系统调用
- CentOS 7拨号上网(ADSL & PPPoE)
- Apache Ant安装与配置
- Mvp架构的使用
- SM2算法第二十五篇:ECDSA数字签名算法原理与实现
- 关于Ant的全面了解
- SM2算法第二十四篇:谈谈PBOC3.0中使用的国密SM2算法
- GPU与GPGPU泛淡
- SM2算法第二十三篇:openssl库中BIGNUM的使用
- shell脚本中单引号和双引号的区别
- iTop-4412_开发板Linux编程之TFTP服务器学习笔记