亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

iOS App使用GCD導致的卡頓現象及解決方法

2019-10-21 18:40:47
字體:
來源:轉載
供稿:網友

最近在調研 iOS app 中存在的各種卡頓現象以及解決方法。

iOS App 出現卡頓(stall)的概率可能超出大部分人的想象,尤其是對于大公司旗艦型 App。一方面是由于業務功能不停累積,各個產品團隊之間缺乏協調,大家都忙著增加功能,系統資源出現瓶頸。另一方面的原因是老設備更新換代太慢,iOS 設備的耐用度極好,現在還有不少 iPhone 4S 在服役,iPhone 6 作為問題設備持有量很高,據估計,現在 iPhone 6s 以前的設備占有比高達 40%。

所以,如果嘗試在線上 App 加入卡頓檢測的工具,你會發現卡頓出現的概率高的驚人。但卡頓的檢測就修復并不簡單,主要是因為難以在開發設備上復現。

之前寫過一篇介紹主線程卡頓監控的文章,好像現在主流的做法都是通過監控 Runloop 事件回調,檢查進入回調的時間間隔是否超過 Threshold,超過則記錄當前 App 所有線程的 call stack。

我前段時間從后臺上報的卡頓日志里看到這樣一個 call stack:

> 0 libsystem_kernel.dylib __workq_kernreturn
> 1 libsystem_pthread.dylib _pthread_workqueue_addthreads
> 2 libdispatch.dylib _dispatch_queue_wakeup_global_slow
> 3 libdispatch.dylib _dispatch_queue_wakeup_with_qos_slow
> 4 libdispatch.dylib dispatch_async

也就是說卡頓出現在 dispatch_async,以我現有對于 GCD 的認知,dispatch_async 是絕無可能出現卡頓的。dispatch_async 的主要任務是從系統線程池里取出一個工作線程,并將 block 放到該線程里去執行。

上述 call stack 確確實實的出現了,而且樣本數量還不少,最后一個函數明顯是一個內核調用。從函數名字猜測,可能是 GCD 嘗試從線程池里獲取線程,但已有線程都在執行狀態,所以向系統內核申請創建新的線程。但創建線程的內核調用會很慢嗎?會慢到讓主線程出現卡頓的程度?帶著疑問我搜索了大量相關資料,最后比較相關的有這樣一篇文章:http://newosxbook.com/articles/GCD.html

其中有這樣一段話:

This isn't due to 10.9's GCD being different - rather, it demonstrates the true asynchronous nature of GCD: The main thread has yet to return from requesting the worker (which it does by pthread_workqueue_addthreads_np, as I'll describe later), and already the worker thread has spawned and is mid execution, possibly on another CPU core. The exact state of the main thread with respect to the worker is largely unpredictable.

作者認為,GCD 申請到的線程有可能是一個正在處理其他任務的 thread,main thread 需要等待這個忙碌的線程返回才能繼續執行,我對這種說法存疑。

最后求助無門的狀況下,我決定使用一次寶貴的 TSL 機會,直接向 Apple 的工程師求教。這里不得不提下,向 Apple 尋求 technical support 是非常寶貴而且可行的方案,每個開發者賬號每年都有 2 次機會,不用非??上?。

我把問題拋過去后,得到一位 Apple 內核團隊工程師的回復,我將精簡過的回復以問答的形式展示和大家分享:

Q: looks like even if it's async dispatching, the main thread still has to wait for the other thread to return, during which time, the other thread happen to be in mid execution of sth. this confuses me, what exactly is the main thread waiting for?

為什么主線程需要等待 dispatch_async 返回,主線程到底在等待什么?

A: It's hard to say with just a user space backtrace. Frame 0 has clearly sent the current thread into the kernel, and this specific kernel call is /way/ too complex to analyse from outside [1].

從用戶態調用棧無法得出答案,內核可能的狀態過于復雜。

Q: I know it's suggested that we create limited amount of serial queue,and use target queue probably. but what could happen if we don't follow that rule?

Apple 一直推薦自己創建 serial GCD queue 的時候,一定要控制數量,而且最好設置 target queue,否則會出現問題,但會出現什么問題我一直很好奇,這次借著機會一起問了。

A:

* On macOS, where the system is happier to over commit, you end up with a thread explosion. That in turn can lead to problems running out of memory, running out of Mach ports, and so on.* On iOS, which is not happy about over committing, you find that the latency between a block being queued and it running can skyrocket. This can, in turn, have knock-on effects. For example, the last time I looked at a problem like this I found that `NSOperationQueue` was dispatching blocks to the global queue for internal maintenance tasks, so when one subsystem within the app consumed all the dispatch worker threads other subsystems would just stall horribly.Note: In the context of dispatch, an “over commit” is where the system had to allocate more threads to a queue then there are CPU cores. In theory this should never be necessary because work you dispatch to a queue should never block waiting for resources. In practice it's unavoidable because, at a minimum, the work you queue can end up blocking on the VM subsystem.Despite this, it's still best to structure your code to avoid the need for over committing, especially when the over commit doesn't buy you anything. For example, code like this:group = dispatch_group_create();for (url in urlsToFetch) {  dispatch_group_enter(group);  dispatch_async(dispatch_get_global_queue(…), ^{    … fetch `url` synchronously …    dispatch_group_leave(group);  });}dispatch_group_wait(group, …);is horrible because it ties up 10 dispatch worker threads for a very long time without any benefit. And while this is an extreme example — from dispatch's perspective, networking is /really/ slow — there are less extreme examples that are similarly problematic. From dispatch's perspective, even the disk drive is slow (-:

這段回復很有意思。閱讀過 GCD 源碼的同學會知道,所有默認創建的 GCD queue 都有一個優先級,但其實每個優先級對應兩個 queue,比如一個是 default-priority, 那么另一個就是 default-priority-overcommit。dispatch_async 的時候,會首先將任務丟進 default-priority 隊列,如果隊列滿了,就轉而丟進 default-priority-overcommit。

在 Mac 系統里,GCD 允許 overcommit,意味著每次 dispatch_async 都會創建一個新線程,即使 over commit 了,這些過量的線程會根據優先級來競爭 CPU 資源。

而在 iOS 系統里,GCD 會控制 overcommit,如果某個優先級隊列 over commit 里,那么排在后面的任務就會處于等待狀態。移動設備 CPU 資源比較緊張,這種設計合乎常理。

所以如果在 iOS 里創建過多的 serial queue,那么后面提交的任務可能就會一直處于等待狀態。這也是為什么我們需要嚴格控制 queue 的數量和層級關系,最好是 App 當中每個子系統只能分配固定數量和優先級的 queue,從而避免 thread explosion 導致的代碼無法及時執行問題。

Q:I know the system watchdog can kill an app if the main thread is taking too long to respond. I also heard rumors that there are two other cases that may gets your app killed by watchdog. the first is too many new threads are being created like by random usage of dispatching work to global concurrent queue? the second case is if CPU has been kept too busy like 100% for too long, watchdog kills app too?

我借機問了下系統 watchdong 強殺 App 的原因,因為坊間一直有傳聞是除了主線程長時間沒反應之外,創建過多的線程和 CPU 長時間超負荷運轉也會導致被強殺。

A:I'm not aware of any specific watchdog check along those lines, but it's not hard to imagine that the above-mentioned knock-on effects might jam up your app sufficiently for the watchdog to kill it for other reasons. Running the CPU for too long generates a crash report but it doesn't actually kill the app. It's essentially a ‘warning' crash report about the problem.

創建過多線程不會直接導致 watchdog 強殺,但過多線程有可能導致主線程得不到及時處理,而因為其他原因被 kill。而 CPU 長時間過載并不會導致強殺,但系統會生成一個 report 來警告開發者。我確實看到過不少這類 ‘this is not a crash' 的 crash 日志。

另外還有一些問答,和我當前疑問并不直接相關所以略去。最后再貼一段比較有意思的回復,在閱讀之前大家可以自己先思考下:

dispatch_async(myQueue, ^{ // line A});// line B

line A 和 line B 誰先執行?

Consider a snippet like this:dispatch_async(myQueue, ^{ // line A});// line Bthere's clearly a race condition between lines A and B, that is, between the `dispatch_async` returning and the block running on the queue. This can pan out in multiple ways, including:* If `myQueue` (which we're assuming is a serial queue) is busy, A has to wait so B will definitely run before A.* If `myQueue` is empty, there's no idle CPU, and `myQueue` has a higher priority then the thread that called `dispatch_async`, you could imagine the kernel switching the CPU to `myQueue` so that it can run A.* The thread that called `dispatch_async` could run out of its time quantum after scheduling B on `myQueue` but before returning from `dispatch_async`, which again results in A running before B.* If `myQueue` is empty and there's an idle CPU, A and B could end up running simultaneously.

答案

其實最后我也沒有得到我想要的準確的答案,可能正如回復里所說,情況有很多而且過于復雜,沒法通過一個用戶態的 call stack 簡單推知內核的狀態,但有些有價值的信息還是得以大致理清:

信息一

iOS 系統本身是一個資源調度和分配系統,CPU,disk IO,VM 等都是稀缺資源,各個資源之間會互相影響,主線程的卡頓看似 CPU 資源出現瓶頸,但也有可能內核忙于調度其他資源,比如當前正在發生大量的磁盤讀寫,或者大量的內存申請和清理,都會導致下面這個簡單的創建線程的內核調用出現卡頓:

libsystem_kernel.dylib __workq_kernreturn

所以解決辦法只能是自己分析各 thread 的 call stack,根據用戶場景分析當前正在消耗的系統資源。后面也確實通過最近提交的代碼分析,發現是由于增加了一些非常耗時的磁盤 io 任務(雖然也是放在在子線程),才出現這個看著不怎么沾邊的 call stack。revert 之后卡頓警報就消失了。

信息二

現有的卡頓檢測工具都只能在超時的情況下 dump call stack,但出現超時有可能是任務 A,B,C 共同作用導致的,A 和 B 可能是真正耗時的任務,C 不耗時但碰巧是最后一個,所以被當成元兇,而 A 和 B 卻沒有出現在上報日志里。我暫時也沒有想到特別好的解決辦法。很明顯,libsystem_kernel.dylib __workq_kernreturn 就是一個不怎么耗時的 C 任務。

信息三

在使用 GCD 創建 queue,或者說一個 App 內部使用 GCD 執行子線程任務時,最好有一套 App 所有團隊都能遵循的隊列使用機制,避免創建過多的 thread,而出現意料之外的線程資源緊缺,代碼無法及時執行的情況。這很難,尤其是在大公司動則上百人的團隊里面。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到IOS開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美精品在线免费| 欧美一区二区三区……| 日韩亚洲综合在线| 91国语精品自产拍在线观看性色| 黑人狂躁日本妞一区二区三区| 欧美日韩激情视频| 日韩在线精品视频| 日韩电视剧免费观看网站| 国外成人在线播放| 欧美中文字幕在线播放| 国产精品视频在线观看| 欧美激情xxxx| 毛片精品免费在线观看| 亚洲在线免费视频| 亚洲最大福利网站| 97视频在线免费观看| 午夜精品一区二区三区在线视频| 琪琪亚洲精品午夜在线| 精品国偷自产在线视频| 中文字幕国产精品| 欧美黑人巨大xxx极品| 亚洲精品视频二区| 91精品久久久久| 亚洲美女又黄又爽在线观看| 欧美成人手机在线| 成人免费午夜电影| 久久亚洲欧美日韩精品专区| 菠萝蜜影院一区二区免费| 亲爱的老师9免费观看全集电视剧| 日韩中文字幕在线精品| 成人久久一区二区三区| 久久99视频免费| 国产欧美精品va在线观看| 51精品在线观看| 国模gogo一区二区大胆私拍| 国产精品久久久久久婷婷天堂| 91影院在线免费观看视频| 久久久久久久久电影| 国产精品高清在线观看| 国产精品日本精品| 欧美性猛交xxxx乱大交| 久久婷婷国产麻豆91天堂| 欧美精品videosex牲欧美| 91亚洲精品视频| 日韩在线高清视频| 日韩一二三在线视频播| 国产午夜精品视频| 不卡在线观看电视剧完整版| 欧美亚洲一级片| 欧美日韩国产中文字幕| 一区二区成人精品| 欧美性猛交丰臀xxxxx网站| 日韩av一区在线| 国内外成人免费激情在线视频| 亚洲国产欧美精品| 久久精品国产精品亚洲| 久久久电影免费观看完整版| 91免费看片网站| 亚洲色图五月天| 一区二区日韩精品| 日韩精品免费看| 福利视频一区二区| 成人免费看吃奶视频网站| 日韩电影免费观看在线观看| 日韩在线观看免费高清完整版| 亚洲欧美成人一区二区在线电影| 国产成+人+综合+亚洲欧美丁香花| 欧美一级成年大片在线观看| 亚洲国产精品99久久| 日韩高清av在线| 成人中文字幕+乱码+中文字幕| 亚洲白虎美女被爆操| 久久久成人精品视频| 日韩电影大全免费观看2023年上| 国产精品久久久久久久久久三级| 国产97在线|日韩| 国产精品第10页| 爽爽爽爽爽爽爽成人免费观看| 亚洲美女黄色片| 欧美小视频在线观看| 国产精品精品一区二区三区午夜版| 欧美高清第一页| 久久久久在线观看| 亚洲成av人影院在线观看| 神马久久久久久| 动漫精品一区二区| 国产黑人绿帽在线第一区| 欧美成年人在线观看| 奇米一区二区三区四区久久| 日韩二区三区在线| 日韩va亚洲va欧洲va国产| 欧美xxxx综合视频| 精品国偷自产在线视频| 麻豆国产精品va在线观看不卡| 最新91在线视频| 亚洲免费人成在线视频观看| 亚洲免费视频在线观看| 日韩亚洲欧美成人| 青青草成人在线| 亚洲国产欧美一区| 日韩欧美国产成人| 成人免费激情视频| 国产在线观看一区二区三区| 一本色道久久88综合亚洲精品ⅰ| 欧美激情精品久久久久久大尺度| 国产综合福利在线| 国产主播欧美精品| 国产91在线播放精品91| 国产精品久久久久av| 欧美精品福利在线| 国产一区二区三区在线观看视频| 韩剧1988在线观看免费完整版| 亚洲视频网站在线观看| 亚洲国产小视频| 欧美性感美女h网站在线观看免费| 亚洲人成亚洲人成在线观看| 亚洲护士老师的毛茸茸最新章节| 欧美国产日韩二区| 精品国产福利视频| 欧美刺激性大交免费视频| 成人美女免费网站视频| 亚洲精品狠狠操| 亚洲色图综合网| 亚洲欧美日本另类| 九九热这里只有精品免费看| 久久人人爽人人爽爽久久| 亚洲自拍中文字幕| 欧美一级淫片videoshd| 国产精品永久免费观看| 亚洲色图综合网| 国产91精品不卡视频| 国产精品久久久久av| 亚洲理论片在线观看| 成人网欧美在线视频| 亚洲综合中文字幕在线观看| 亚洲第一av在线| 久久九九精品99国产精品| 精品视频一区在线视频| 午夜欧美不卡精品aaaaa| 韩剧1988免费观看全集| 久久69精品久久久久久国产越南| 日本aⅴ大伊香蕉精品视频| 欧美日在线观看| 97超视频免费观看| 欧美资源在线观看| 亚洲国产欧美一区二区三区久久| 亚洲人成毛片在线播放| 在线观看国产精品91| 国产极品jizzhd欧美| 色婷婷综合久久久久中文字幕1| 成人黄色av网站| 45www国产精品网站| 日韩欧美极品在线观看| 韩国精品久久久999| 欧美在线不卡区| 亚洲欧美综合区自拍另类| 日韩欧美在线看| 国产精品第100页| 在线观看欧美日韩| 国内精品模特av私拍在线观看| 国内久久久精品| 日韩精品中文字幕久久臀| 中文字幕一精品亚洲无线一区| 欧美第一淫aaasss性|