runloop和时间片

来源:互联网 发布:mac 视频播放器 编辑:程序博客网 时间:2024/04/29 11:02
前一篇<<postNotificationName同步调用导致的白屏问题>>里讲到"mediaView里抛通知时在异步线程抛,利用线程切换避免reloadData过程中再reloadData”.
为何这样做可以解决问题呢?

在异步线程(假设是thread10)抛HWCHAT_VIEW_NEED_RELOAD通知,导致在thread10里同步调用了reloadChatCollectionViewData。因为在reloadChatCollectionViewData里进行了线程切换:yoho_dispatch_execute_in_main_queue, 切回到
主线程执行reloadData。
这样执行reloadData为何避免了reloadData过程中再reloadData呢? 
下面涉及到runloop的问题。

看出问题时的log:
2015-03-25 10:44:40:168 demo[2957:807] Debug|[HWChatManager.m 175]: __45-[HWChatManager reloadChatCollectionViewData]_block_invoke 抛通知HWCHAT_VIEW_NEED_RELOAD
2015-03-25 10:44:40:168 demo[2957:807] Debug|[HWChatViewController.m 162]: -[HWChatViewController reloadChatCollectionViewData] 111111*********
2015-03-25 10:44:40:169 demo[2957:807] Debug|[HWChatViewController.m 164]: __52-[HWChatViewController reloadChatCollectionViewData]_block_invoke 22222==========
2015-03-25 10:44:40:206 demo[2957:807] Debug|[HWPhotoMediaItem.m 41]: __29-[HWPhotoMediaItem mediaView]_block_invoke_2 抛通知HWCHAT_VIEW_NEED_RELOAD
2015-03-25 10:52:59:275 demo[2957:807] Debug|[HWChatViewController.m 162]: -[HWChatViewController reloadChatCollectionViewData] 111111*********
2015-03-25 10:52:59:275 demo[2957:807] Debug|[HWChatViewController.m 164]: __52-[HWChatViewController reloadChatCollectionViewData]_block_invoke 22222==========
2015-03-25 10:52:59:277 demo[2957:807] Debug|[HWChatViewController.m 167]: __52-[HWChatViewController reloadChatCollectionViewData]_block_invoke 33333+++++++++++

807是主线程id,在主线程调用了[HWChatViewController reloadChatCollectionViewData],reloadData时导致[HWPhotoMediaItem mediaView]在主线程抛通知HWCHAT_VIEW_NEED_RELOAD,[HWChatViewController reloadChatCollectionViewData]做为观察者的selector又执行了一次reloadData导致白屏。
这些动作都是在主线程的runloop的一次pass(循环的一次遍历,暂命名passA)里执行的。

主线程中, mediaView里抛通知时改为在异步线程抛。那么,进程需要等到线程切换时,才能切到异步线程thread10。

进程得到时间片进行线程切换,进入线程10.
thread10通知HWCHAT_VIEW_NEED_RELOAD, [HWChatViewController reloadChatCollectionViewData]做为观察者的selector在thread10中执行:
-(void)reloadChatCollectionViewData {    HWLog(@"111111*********");    yoho_dispatch_execute_in_main_queue(^{        HWLog(@"22222==========");        [_collectionView reloadData];        [_collectionView layoutIfNeeded];        HWLog(@"33333+++++++++++");        [_collectionView scrollToBottomAnimated:YES];    });}



然后reloadChatCollectionViewData里yoho_dispatch_execute_in_main_queue又切回到主线程。
这些动作是在thread10的runloop的一次pass(假设是passB)里执行的


那么,进程需要等到线程切换时,才能切到主线程执行reloadData
进程得到时间片进行线程切换,再进入主线程执行passC。此时passA早已经执行完成,即之前的reloadData已完成执行。
那么passC中执行reloadData当然和之前的reloadData没有关系了,避免了"reloadData过程中再reloadData”。
无问题时的log如下:
2015-03-25 11:50:52:145 demo[10982:807] Warn|[HWRecentTableViewController.m 214]: -[HWRecentTableViewController reloadQLMessage] 重新加载列表
2015-03-25 11:50:52:748 demo[10982:ce17] Debug|[HWPhotoMediaItem.m 41]: __29-[HWPhotoMediaItem mediaView]_block_invoke_2 抛通知HWCHAT_VIEW_NEED_RELOAD
2015-03-25 11:50:52:748 demo[10982:2507] Debug|[HWPhotoMediaItem.m 41]: __29-[HWPhotoMediaItem mediaView]_block_invoke_2 抛通知HWCHAT_VIEW_NEED_RELOAD
2015-03-25 11:50:52:748 demo[10982:ce17] Debug|[HWChatViewController.m 164]: -[HWChatViewController reloadChatCollectionViewData] 111111*********
2015-03-25 11:50:52:748 demo[10982:2507] Debug|[HWChatViewController.m 164]: -[HWChatViewController reloadChatCollectionViewData] 111111*********
2015-03-25 11:50:52:750 demo[10982:807] Debug|[HWChatViewController.m 166]: __52-[HWChatViewController reloadChatCollectionViewData]_block_invoke 22222==========
2015-03-25 11:50:52:777 demo[10982:807] Debug|[HWChatViewController.m 169]: __52-[HWChatViewController reloadChatCollectionViewData]_block_invoke 33333+++++++++++
2015-03-25 11:50:52:777 demo[10982:807] Debug|[HWChatViewController.m 166]: __52-[HWChatViewController reloadChatCollectionViewData]_block_invoke 22222==========
2015-03-25 11:50:52:804 demo[10982:807] Debug|[HWChatViewController.m 169]: __52-[HWChatViewController reloadChatCollectionViewData]_block_invoke 33333+++++++++++


最后再描述一下runloop. 为了好解释,以单cpu情形为例。
程序能不断地处理用户点击或者网络事件, 是因为runloop的驱动,不然,cpu执行完指令不是应该立刻退出程序了吗?
runloop可以理解为你用手臂在水里不断划圈,驱动了水的转动(程序的执行)。你的手臂划一个完整的圆圈,这一个圆圈,就是上面提的一个pass。

理解runloop也可以结合cpu时间片。
进程得到cpu时间片后,在某个线程的runloop的某次pass里执行,执行时如果遇到网络请求或磁盘io或其他原因导致出让cpu,此次pass结束执行。
后面进程再获得cpu时间片得以运行时,就是在下一个pass里执行了。


0 0
原创粉丝点击