[問題] 有沒有法子找出程式為何無法中止?
今天改的版本,突然間陷入 while loop 無法中止
強迫中止時錯誤訊息是
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/
threading.py", line 1388, in _shutdown
lock.acquire()
這種訊息有和沒有差不多
有 lock 沒有釋放?
於是我寫了個 MyLock 取代 threading.Lock
開始記錄到底是哪個 Lock 有進入而沒有退出
全都有退出!根本查不到
最後是透過 git 不斷捲回舊版,找到十二小時前的版本還可以正常退出程式
一查,重點不在程式,在資料!
某一種資料格式,我會啟動 Timer, 而 Timer 時間到後,我會再重啟 Timer
(其實就是連續脈衝波,每隔幾秒一次的意思)
因為我預約了下一次的執行需求,所以程式無法釋放
只要 cancel timer, 就解掉這個 bug 了
(bug 是一直存在的,只是一直沒鍵入這種資料凸顯 bug)
還真是忘了,在 Python 的 Timer 是以 Thread 來實現
而骨子裡可能設了個 lock
那我問題來了:這次是幸運在 git 裡保存夠多細節,且能捲回一定能釋放的舊版
假設沒這樣的環境,只能靠我自己抓,而錯誤訊息又只說有 lock 沒釋放
我能回溯知道是哪一行程式產生的 lock 鎖住了嗎?
有什麼參數,什麼 debug tool 能用嗎?
謝謝
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 116.241.233.114 (臺灣)
※ 文章網址: https://www.ptt.cc/bbs/Python/M.1680299059.A.BEA.html
→
04/01 11:21,
3年前
, 1F
04/01 11:21, 1F
RLock 是允許重複進入,避免同一個 thread 自己把自己卡住
而我的要求是回應卡住的物件是在哪行指令生成
舉例來說
Line 10: a = new C1() # test.c
這是產生物件;目前都有自動釋放,但假如在沒自動釋放的 C 語言
而且我沒有釋放物件,造成記憶體浪費,
有工具能回應我,浪費的記憶體在 test.c/Line 10 嗎?
我還真用過這種工具
→
04/01 12:27,
3年前
, 2F
04/01 12:27, 2F
哇!我試試! 謝謝
... 超過我的程度 XD
裝了,跑不起來..
※ 編輯: HuangJC (116.241.233.114 臺灣), 04/01/2023 15:21:44
推
04/02 17:33,
3年前
, 3F
04/02 17:33, 3F
推
04/03 20:03,
3年前
, 4F
04/03 20:03, 4F
→
04/03 20:16,
3年前
, 5F
04/03 20:16, 5F
→
04/05 17:35,
3年前
, 6F
04/05 17:35, 6F
當年我用過 debug tool 抓記憶體漏洞之類,真的過癮
但有種很怪的感覺,那是高手在替我做事,而且能力是有限的
比如找尋未釋放記憶體的原理是什麼?
a = new C1() // 配發記憶體 a
del a // 釋回 a
如果 a 忘記釋放,要怎麼找出 a 在哪一行配發的?
畢竟 a 這個符號可能被重覆配發,也可能
b = a // 被 b 承接走
a = NULL // 棄用 a 這個符號,但 b 忘了釋放 XD
因此這種 tool 能做的只有。。當初 a 配發時,就把 debug info 順便存入
比如覆寫 new 這功能,成為 DEBUG_NEW
然後隱性的傳入程式的資訊(利用 __FILE__, __LINE__ 這種 compiler 維護的符號)
這也代表配發記憶體時,我本來只配幾個 byte, 但可能同時配發更多的 debug byte
因此 debug 時的速度會變慢,耗用記憶體會增加
但高手替我抓還是很好用 XD
等我程式寫太大時,這種 tool 因為要多耗用很多 debug 資源,就會跑不動
最佳方案仍然是自己寫 debug tool
但我如果功力高到會寫 debug tool,我當初就不會犯錯了!
先有雞還是先有蛋的問題
--------
總之我這次學到了,Timer 元件其實要注意它的配發和釋放
※ 編輯: HuangJC (123.204.157.162 臺灣), 04/06/2023 01:08:28
推
04/06 23:27,
3年前
, 7F
04/06 23:27, 7F
→
04/06 23:27,
3年前
, 8F
04/06 23:27, 8F
是遇到了才知道遇到了啊..
我一開始可超級謹慎的
class MyApp(App):
def f1(self):
self.destroy()
app = MyApp()
pApp = weakref.proxy(app)
btn = PushButton(app, command=pApp.f1)
app.display()
比如我曾這樣寫程式,其中 pApp 根本沒必要
為什麼這樣寫?因為我怕 btn 物件裡如果保有 app
因為參考到自己,會導致永不釋放
所以凡參考我都只留 weak 版本
後來我發現不這麼寫,照樣不會卡,照樣釋放,想法可以很單純 XD
我從完全沒有 gc 的 C 語言學起的,從前記憶體管理都自己來
那時如果釋放了不把變數標成 null, 再叫用到就 null access
能碰到的問題可非常多
a = new C1();
b = a; // 比如進入函式,也是拷走一個 a 指標在函式裡用
del b; // 除非我不在函式裡釋放物件,否則當然會寫這種東西
a.xxx // 這時外面還想取用 a pointer 就慘了,null access
後來開始用有 gc 的語言,可是參照次數很重要
如果還有任何指標參照到物件,物件就不會釋放
那循環參照怎麼算?還真是測了才知道
一般不會遇到?不是,我一開始學時曾經有遇到,但不知道就寫了兩年
舉例來說,樹莓派一開機就跑我的程式,但從沒想過要關機
執行完畢直接斷電,就算程式無法釋放,都斷電了是有何差別?
只有刻意去想,我就是要讓程式釋放,才會測試到這件事
比如我可能要寫遠端更新,要讓程式結束並且重開機
喔,還是有很暴力的寫法,比如下個 kill process 指令
在程式未能退出的情況下,由 os 端殺掉 XD
會去注意是潔癖吧..
總覺得,類似檔案沒有 close 等循序退出問題,如果沒做,很不舒服
別人沒遇到嗎?
比如我在寫 ios 手機時,用的是 obj c
我也下載過別人的函式庫用
我看別人防這個也防得很嚴
一堆 weaf ref 在用
應該說,為什麼 python 在參照到自己的指標留著時還能釋放
這才是超乎我預期的 XD
所以 Timer 在幾秒後才要啟動,而它啟動時要用到的指標要不要 weak 版?
程式想退出了,幾秒後根本就不存在,若保留指標程式就無法退出
那按鍵按了要退出程式,按鍵的程式裡保留指標,為什麼可以退出 :P
那也是測了才知道。。
QA 不會做這項測試,都專注在跑程式,而不是退出程式 XD
我也是哪天心血來潮才測到的。
那時曾經呼叫同事寫的副程式,用 try-catch 包起來呼叫
甚至是每次呼叫前才 load library (本來可以用靜態寫的,不必這麼麻煩)
呼叫後就 free library
為什麼要這樣做?因為同事記憶體釋放沒寫好啊,我整個 free 掉才會乾淨
所以一般是沒遇到,還是沒注意到?
※ 編輯: HuangJC (123.204.157.162 臺灣), 04/07/2023 01:00:44
甚至我想過一個問題:參照到自己,或說交互參照
這種還有法子釋放資源是怎麼做的?
(同事是說有很多論文,不過我不擅長查)
如果說這機制有 bug
會不會我剛用沒問題,但用多了,資源就變無法釋放?
那這不是我有問題,是寫釋放的有 bug, 沒確實 implement 出來
看過是說 gc 不會時時刻刻,而是久久做一次
所以我以前去主動檢查有沒有釋放,也常沒釋放
必需在主動檢查前,先主動催 gc 做一次垃圾搜集
會不會就是因為這樣,我下載的第三方函式庫才廣用 weak 版本?
→
04/07 18:10,
3年前
, 9F
04/07 18:10, 9F
→
04/07 18:10,
3年前
, 10F
04/07 18:10, 10F
→
04/07 18:12,
3年前
, 11F
04/07 18:12, 11F
→
04/07 18:12,
3年前
, 12F
04/07 18:12, 12F
→
04/07 18:12,
3年前
, 13F
04/07 18:12, 13F
→
04/07 18:12,
3年前
, 14F
04/07 18:12, 14F
推
04/07 18:28,
3年前
, 15F
04/07 18:28, 15F
a 我 id 其實沒幫助
我直說好了,有人說我半桶水
那我能怎樣?膨風說自己全都懂?
前不久和另一位也是資深工程師聊這邊的狀況
聊到有人叫我去讀 os,他直接回我,他也從業幾十年,但從沒讀過 os
所以我們只能這麼說:底線就是,這本來就是我的責任
資訊不夠,多少是因為我不能把公司產品全丟到網路上,source code 公開
所以得用各種抽象的方式,只曝露一些點來討論
若實在是不行也就算了
→
04/07 19:42,
3年前
, 16F
04/07 19:42, 16F
推
04/08 09:54,
3年前
, 17F
04/08 09:54, 17F
→
04/08 09:54,
3年前
, 18F
04/08 09:54, 18F
→
04/08 09:54,
3年前
, 19F
04/08 09:54, 19F
推
04/08 09:57,
3年前
, 20F
04/08 09:57, 20F
→
04/08 09:57,
3年前
, 21F
04/08 09:57, 21F
這我知道,知道還這麼做,就代表混入 debug 等心得..
當年寫 c,寫到某個地方,我說懷疑 compiler 有 bug
也是被人狠狠嘲笑
幾年後看到臉書上另一個朋友說,c compiler 有一大堆 bug...
咦,原來也有別人遇到,那我被笑又怎麼回事;只能說有沒有人同樣見識到了
責任不能推到 compiler 去,但我們得去驗證這些問題存不存在
所以對這些都是有限信任
obj c 也是有 gc 的 (應該說隨著年代, 很多語言的特性互相學習)
而它若是自我參照時,就不會釋放!
這我能寫一個極小的程式驗證到
發生時,就要'打破這個環節'
在 obj c 我也不會去 del, 我是把指標設為 None
這樣循環參照就打破了
(我這篇裡用到 del, 但那是 c 啊,先還原到最原始,沒有 gc 的環境描述問題)
去說明這些我知道似乎也沒有意義
知道我也是碰到這些 bug 了
推
04/08 11:10,
3年前
, 22F
04/08 11:10, 22F
推
04/08 11:50,
3年前
, 23F
04/08 11:50, 23F
→
04/08 11:50,
3年前
, 24F
04/08 11:50, 24F
→
04/08 11:51,
3年前
, 25F
04/08 11:51, 25F
Timer 是一種 Thread 這我知道
我不知道的是我啟動了 lock
lock 一般來說,是 multi-thread 時,
某 thread 要求獨佔資源,其它 thread 不能進入
而我使用 Timer 時,我沒要獨佔什麼,所以我沒想到它啟動了 lock
或者,這問題不是 Timer 啟動了 lock
我程式中本來就用了很多 lock, 憑最後出現的錯誤訊息,我並不知是哪個 lock 未釋放
不然改個說法:我能不能把每個 lock 命名
當程式用強迫退出的方式並且收到錯誤訊息
能夠知道是哪個 lock 沒有釋放
※ 編輯: HuangJC (116.241.233.114 臺灣), 04/08/2023 16:24:15
推
04/08 16:42,
3年前
, 26F
04/08 16:42, 26F
→
04/08 16:42,
3年前
, 27F
04/08 16:42, 27F
→
04/08 16:44,
3年前
, 28F
04/08 16:44, 28F
蠻常 hack 的,但還是不行,主要是猜測行為都猜錯了,就無法看著 code 想像
來看這麼短的程式就好
from guizero import App, PushButton
app = App()
btn = PushButton(app, command=app.destroy)
app.display()
這程式的目的只有一個:產生一個按鍵,按了就退出程式
我本來以為這不會正常運作的
理由是 btn 是個物件,它裡面有變數參照了 app.destroy
那這樣 app 的 ref count 不是會加一嗎?
不用執行,光有變數拷走我的指標,就要加一了,沒錯吧..
不然萬一要執行時 app 不存在怎麼辦?
按下按鍵,我以為程式要卡死不能退出,但它就是順利退出了
我就是以為,這個 gc 可以自行解決啊?
結果 timer 的倒沒自行解決,得由我來 cancel timer!
※ 編輯: HuangJC (116.241.233.114 臺灣), 04/09/2023 13:21:02
推
04/09 13:50,
3年前
, 29F
04/09 13:50, 29F
→
04/09 14:51,
3年前
, 30F
04/09 14:51, 30F
→
04/09 15:54,
3年前
, 31F
04/09 15:54, 31F
還有 22 則推文
還有 7 段內文
推
04/16 15:32, , 54F
04/16 15:32, 54F
→
04/16 15:33, , 55F
04/16 15:33, 55F
→
04/16 15:33, , 56F
04/16 15:33, 56F
→
04/16 15:35, , 57F
04/16 15:35, 57F
我不是說了嘛,我是 bug 已經解了,回頭看錯誤訊息,覺得錯誤訊息沒幫助
我可以把程式寫個很精簡的小範例重現 bug
但我還是不懂那和 OS 的關係在哪
何謂 thread? 那我乾脆就默了一段出來
以此代表在 OS 我也是了解這段的(而其他段或許不了解)
既然要說我對 OS 了解不夠
那是不是也默一段出來,說說我哪裡錯了?
噓
04/23 17:25, , 58F
04/23 17:25, 58F
→
04/23 17:27, , 59F
04/23 17:27, 59F
→
04/23 17:28, , 60F
04/23 17:28, 60F
→
04/23 17:29, , 61F
04/23 17:29, 61F
→
04/23 19:22, , 62F
04/23 19:22, 62F
→
04/23 19:22, , 63F
04/23 19:22, 63F
→
04/23 19:24, , 64F
04/23 19:24, 64F
→
04/23 19:25, , 65F
04/23 19:25, 65F
→
04/23 19:25, , 66F
04/23 19:25, 66F
→
04/23 19:25, , 67F
04/23 19:25, 67F
噓
04/23 19:46, , 68F
04/23 19:46, 68F
→
04/23 19:47, , 69F
04/23 19:47, 69F
→
04/23 19:47, , 70F
04/23 19:47, 70F
→
04/23 19:50, , 71F
04/23 19:50, 71F
→
04/23 19:50, , 72F
04/23 19:50, 72F
→
04/23 19:55, , 73F
04/23 19:55, 73F
→
04/23 19:56, , 74F
04/23 19:56, 74F
→
04/23 19:56, , 75F
04/23 19:56, 75F
不見得會
C 再熟,看別人程式也會需要重新熟悉
又不是別人程式用 C 寫的,而我寫 C 久了,我就一定能看懂
如果你是一定能看懂別人程式,其實你很優秀 :P
老實說我很久沒碰 C 了,但對 C 的語法一直是還在參考
因為一堆語言是 c like 啊,C 應該算我的基礎
gc, 垃圾搜集,c 沒有嗎?
(啊,還真難一點,改說 c++ 好了)
在建構,解構式裡,埋 reference count 這些
其實也是慢慢發展出來的
更別說同樣是 c, 換了一套 ide, 結果環境不熟
但基本就是一樣,step, trace into..
所以從 python 的直譯器去,會很快見到問題?
這想法有點怪,有點在 python 解不了,去找外掛的感覺
就好像 c 看程式解不了,去找執行檔反組譯成組合語言解的感覺
然後如果我說我組合語言寫十幾年
你回嗆說那當然可以直接看組合語言
啊。。組合語言是懂,但可讀性是那樣子
不在高階語言的高度直接解,不嫌辛苦?
我承認有個時候會需要進組合語言解:
懷疑 c compiler 本身有 bug,要看它做錯什麼時
但如果信任 c compiler, 這事我就不幹了...
→
04/23 20:03, , 76F
04/23 20:03, 76F
→
04/23 20:03, , 77F
04/23 20:03, 77F
→
04/23 20:04, , 78F
04/23 20:04, 78F
→
04/23 20:04, , 79F
04/23 20:04, 79F
→
04/23 20:05, , 80F
04/23 20:05, 80F
→
04/23 20:05, , 81F
04/23 20:05, 81F
→
04/23 20:05, , 82F
04/23 20:05, 82F
→
04/23 20:06, , 83F
04/23 20:06, 83F
※ 編輯: HuangJC (123.204.157.211 臺灣), 05/04/2023 03:40:36
噓
05/06 17:05, , 84F
05/06 17:05, 84F
→
05/06 17:07, , 85F
05/06 17:07, 85F
→
05/06 17:07, , 86F
05/06 17:07, 86F
→
05/06 17:07, , 87F
05/06 17:07, 87F
→
05/06 17:08, , 88F
05/06 17:08, 88F
→
05/06 17:08, , 89F
05/06 17:08, 89F
我學習能力差一樣不用你關心啊
我是什麼重要的人影響到你了嗎?
難道我說你學習能力很好,你能接受?
你沒這麼自大吧。。。
對我來說,我的重點只有'這個我不會'
其他不重要,也沒那麼自大
我相信我有多謙虛,你也有
※ 編輯: HuangJC (123.241.134.39 臺灣), 12/01/2023 21:09:11
Python 近期熱門文章
PTT數位生活區 即時熱門文章