runloop之于thread
做一个技术方向久了,难免会沉溺其中,对当初开始接触这个方向的许多根本上的疑问渐渐都不了了之,意识上认为然,而不知其所以然。
最近重新梳理iOS的runloop,说说自己的理解,希望能说清楚。
先抛出一个问题:程序入口main函数只是一段代码,为什么只运行了一段代码,程序就可以一直运行?
main函数中起了一个main thread,main thread执行结束,则应用程序退出,为了能让线程一直运行,苹果引入了runloop,苹果这样介绍runloop:
Run loops are part of the fundamental infrastructure associated with threads. A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep when there is none.
main thread中的runloop一直在跑圈圈(while循环),所以这个main thread不会立即执行结束,如果设定这个runloop跑圈的时间是1年,app就可以运行1年,1年后app自动退出。
这里面有两个问题:
- runloop如果一直在跑圈,会一直占用/消耗系统资源,怎么避免?
- 跑圈圈的同时,如果用户有输入等操作,怎么检测?
runloop是基于事件处理的,有事件需要处理,则runlopp开始跑圈检测需要处理的事件,处理结束后,进入休眠,当再次有事件需要处理时,再次被唤醒去跑圈,如此反复。
怎么知道有事件要去处理呢?事件包括许多,计时器、用户输入、点击、网络请求等,main thread中创建main runloop时,会将这些事件作为source加入到runloop中,这样,当其中一个source有变更时,激活runloop去跑圈。当然如果这些runloop中source都被移除了,runloop跑一圈之后就会被释放,线程也随即结束。如果main runloop的source可以全部移除,那就意味着app要退出了,但实际的情况是,main runloop被系统做了特殊处理,是无法真正把source都remove掉的,想通过移除source达到app exit的目的是不行的。
做移动开发的同学经常会被问到iOS系统为什么比Android系统流畅的问题,问题也出在runloop中。runloop中有几种模式,其中一种叫Event tracking模式,意为事件追踪,当用户滚动页面等操作时,runloop会被切换到追踪模式,这个模式会限制后续其他事件,比如在这个模式下,点击、长按等其他事件等都将会被限制,让系统只专注处理当前tracking的事件。整个系统只专注做一件事,当让流畅。
题为runloop之于thread,是说它们之间的关系,thread可以没有runloop,但runloop必须运行在线程中,也就是是apple说的The purpose of a run loop is to keep your thread busy 。thread和runloop是一一对应的,以键值对存储在底层,一个thread/runloop中可以嵌套多个thread/runloop。
iOS中非常著名的网络库AFNetworking(老版本,大家都喜欢拿这段代码进行解读)有这样一段代码:
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
这段代码创建了一个守护线程,为了让这个线程能够一直在后台处理网络,在线程入口networkRequestThreadEntryPoint: 中通过 [NSRunLoop currentRunLoop]创建了一个runloop;为了runloop能够一直跑圈,向runloop中加入了一个Mach port源,只要这个port源不移除,runloop就会一直存在。
这个时候的runloop其实是一直处于休眠状态,随后开始网络请求时向其中加入了connection源:
- (void)operationDidStart {
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
}
[self.connection start];
}
}
connection收到网络响应后,runloop被激活,开始跑圈,然后检测到有网络响应数据,runloop和connection协调数据输出,以回调的形式将底层接收的数据抛出,
当所有数据传输结束后,connection被AFNetworking置为ni,从runloop中移除,这时runloop进入休眠,这样就完成了一次在后台发请求、接收数据的过程。
等重新开始一次网络请求时,这个过程将会被再次执行一遍。
参考资料
Threading Programming Guide: Run Loops
Threading Programming Guide: Thread Management
runloop之于thread的更多相关文章
- 深入理解RunLoop
网上看的一篇文章,写的真好,我得多看几次好好理解理解 膜拜大神,转载至此便于学习查看. 此处标明原文链接:http://blog.ibireme.com/2015/05/18/runloop/ ...
- 【转】Thread.isBackground
C#中,Thread类有一个IsBackground 的属性.MSDN上对它的解释是:获取或设置一个值,该值指示某个线程是否为后台线程.个人感觉这样的解释等于没有解释. .Net中的线程,可以分为后台 ...
- 浅谈Runloop
RunLoop 是 iOS 和 OS X 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何 ...
- Runloop 深入理解(转)
RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利 ...
- RunLoop的深入了解
RunLoop 是 iOS 和 OS X 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何 ...
- 关于Thread.IsBackground属性的理解(转载)
C#中,Thread类有一个IsBackground 的属性.MSDN上对它的解释是:获取或设置一个值,该值指示某个线程是否为后台线程.个人感觉这样的解释等于没有解释. .Net中的线程,可以分为后台 ...
- 史上最全的RunLoop介绍
之前有人在后台给小编留言,说:小编啥时候给我们分享RunLoop的一些文章,工作以后特别需要这样的技术.这不,小编从网上找了一个介绍非常详细,清晰的文章,仅供参考. RunLoop 是 iOS 和 O ...
- Thread IsBcakgroud
C#中,Thread类有一个IsBackground 的属性.MSDN上对它的解释是:获取或设置一个值,该值指示某个线程是否为后台线程.个人感觉这样的解释等于没有解释. .Net中的线程,可以分为后台 ...
- iOS 深入理解RunLoop
RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利 ...
随机推荐
- 2016CCPC 中南地区邀请赛 A 矩阵快速幂
A A^n=A^(n%2016)%7; #include <iostream> #include <cstdio> #include <cstring> #incl ...
- 《GettingThingsDone》--GTD学习笔记(三)-GTD的三个关键原则
原则一:养成收集的习惯 1.收集习惯给个人带来的好处 在收集过程中你会出现焦虑和解脱,难以招架和控制良好的情绪. (1)消极情绪的来源 要做的事情总比你能做的事情多,要做的事情太多并不 ...
- 部分常用Express方法详解
app.set(name, value) 分配给name一个value,并将name作为app settings table的一个属性. 使用app.set('foo', true) 相当于调用 ap ...
- Petshop学习第二天
数据访问层的数据访问设计 1.数据层的内容: 数据库访问.Messaging.membership.Profile四部分 2.数据库对象的分类: 一类:数据实体,对应数据库中相应的数据表,它们作为数据 ...
- 用Java实现一个通用并发对象池
这篇文章里我们主要讨论下如何在Java里实现一个对象池.最近几年,Java虚拟机的性能在各方面都得到了极大的提升,因此对大多数对象而言,已经没有必要通过对象池来提高性能了.根本的原因是,创建一个新的对 ...
- 集合框架Map之entrySet方法的使用
Map的entrySet函数的使用,取得是键和值的映射关系,Entry就是Map接口中的内部接口,类似与我们熟悉的内部类一样,内部类定义在外部类内部,可以直接访问到外部类中的成员 package cn ...
- iOS7自定义statusbar和navigationbar的若干问题
当然有许多问题是这篇文章中没有提到的,按照文章的方法进行设置,你可能会遇到以下问题: 1.navigationbar的背景图片自定义以后,statusbar虽然和navigationbar共用了背景图 ...
- oracle 修改索引现有表空间
工作日记之<修改索引现有表空间> //dba_indexes可查询所有索引,以及索引部分信息,可以灵活运用于其他用途 //假设用户USER1现有表空间TS1.TS2,需要迁移其下所有表空间 ...
- CMD-NET命令详解(转载)
本文转自http://www.cnblogs.com/chenjq0717/archive/2010/05/09/1730934.html net命令大全,net命令用法,net网络命令,net命令使 ...
- RocketMQ常用命令
转自:http://jameswxx.iteye.com/blog/2091971 1.1. 控制台使用 RocketMQ 提供有控制台及一系列控制台命令,用于管理员对主题,集群,broker 等信息 ...