[問題] 有沒有法子找出程式為何無法中止?
今天改的版本,突然間陷入 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,
1年前
, 1F
04/01 11:21, 1F
RLock 是允許重複進入,避免同一個 thread 自己把自己卡住
而我的要求是回應卡住的物件是在哪行指令生成
舉例來說
Line 10: a = new C1() # test.c
這是產生物件;目前都有自動釋放,但假如在沒自動釋放的 C 語言
而且我沒有釋放物件,造成記憶體浪費,
有工具能回應我,浪費的記憶體在 test.c/Line 10 嗎?
我還真用過這種工具
→
04/01 12:27,
1年前
, 2F
04/01 12:27, 2F
哇!我試試! 謝謝
... 超過我的程度 XD
裝了,跑不起來..
※ 編輯: HuangJC (116.241.233.114 臺灣), 04/01/2023 15:21:44
推
04/02 17:33,
1年前
, 3F
04/02 17:33, 3F
推
04/03 20:03,
1年前
, 4F
04/03 20:03, 4F
→
04/03 20:16,
1年前
, 5F
04/03 20:16, 5F
→
04/05 17:35,
1年前
, 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,
1年前
, 7F
04/06 23:27, 7F
→
04/06 23:27,
1年前
, 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,
1年前
, 9F
04/07 18:10, 9F
→
04/07 18:10,
1年前
, 10F
04/07 18:10, 10F
→
04/07 18:12,
1年前
, 11F
04/07 18:12, 11F
→
04/07 18:12,
1年前
, 12F
04/07 18:12, 12F
→
04/07 18:12,
1年前
, 13F
04/07 18:12, 13F
→
04/07 18:12,
1年前
, 14F
04/07 18:12, 14F
推
04/07 18:28,
1年前
, 15F
04/07 18:28, 15F
a 我 id 其實沒幫助
我直說好了,有人說我半桶水
那我能怎樣?膨風說自己全都懂?
前不久和另一位也是資深工程師聊這邊的狀況
聊到有人叫我去讀 os,他直接回我,他也從業幾十年,但從沒讀過 os
所以我們只能這麼說:底線就是,這本來就是我的責任
資訊不夠,多少是因為我不能把公司產品全丟到網路上,source code 公開
所以得用各種抽象的方式,只曝露一些點來討論
若實在是不行也就算了
→
04/07 19:42,
1年前
, 16F
04/07 19:42, 16F
推
04/08 09:54,
1年前
, 17F
04/08 09:54, 17F
→
04/08 09:54,
1年前
, 18F
04/08 09:54, 18F
→
04/08 09:54,
1年前
, 19F
04/08 09:54, 19F
推
04/08 09:57,
1年前
, 20F
04/08 09:57, 20F
→
04/08 09:57,
1年前
, 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,
1年前
, 22F
04/08 11:10, 22F
推
04/08 11:50,
1年前
, 23F
04/08 11:50, 23F
→
04/08 11:50,
1年前
, 24F
04/08 11:50, 24F
→
04/08 11:51,
1年前
, 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,
1年前
, 26F
04/08 16:42, 26F
→
04/08 16:42,
1年前
, 27F
04/08 16:42, 27F
→
04/08 16:44,
1年前
, 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,
1年前
, 29F
04/09 13:50, 29F
→
04/09 14:51,
1年前
, 30F
04/09 14:51, 30F
→
04/09 15:54,
1年前
, 31F
04/09 15:54, 31F
→
04/09 15:54,
1年前
, 32F
04/09 15:54, 32F
要講這麼籠統的話,建議計概重學,建議 python 重學,不也都說得通?
成語 邯鄲學步 就這意思了
學著學著有沒有更高明?還是反而被搞糊塗了呢?
thread 就我所知,就是系統把目前所有的暫存器通通儲存起來,
然後切換到另一個 thread 的 context 去
之後再切換回來,除了時間點改變之外,CPU 完全可以在這個點繼續往下執行
而兩個 thread 如果有共用變數,且搶著修改,可能混成一團
因此在有必要獨佔一個變數時,就可以用一個同步物件來 lock 使用權
當然隨著應用的方式不同,看起來也許不像鎖著物件,而像鎖著程式區塊
比如 critical section 的應用方式
知道,但解決不了
因為在這個地方忽略而產生 bug, 錯訊訊息不見得是在這個地方
所以我是在請教 debug 技巧
就以我文章最前面所講的問題,我是已經解決了
我是知道解法,再回頭看錯誤訊息,覺得沒有幫助
其實我想要的是在程式強硬退出後,有'列舉所有 thread 狀態'這類指令
所以我已經在打造自己的 debug tool 了
希望可以追蹤 thread 當下的行為,以知道 thread 為何釋放不了
※ 編輯: HuangJC (49.217.174.143 臺灣), 04/10/2023 01:51:25
→
04/10 11:31,
1年前
, 33F
04/10 11:31, 33F
→
04/10 21:25,
1年前
, 34F
04/10 21:25, 34F
→
04/10 21:25,
1年前
, 35F
04/10 21:25, 35F
→
04/10 21:25,
1年前
, 36F
04/10 21:25, 36F
→
04/10 21:25,
1年前
, 37F
04/10 21:25, 37F
→
04/10 22:00,
1年前
, 38F
04/10 22:00, 38F
→
04/10 22:01,
1年前
, 39F
04/10 22:01, 39F
→
04/10 22:02,
1年前
, 40F
04/10 22:02, 40F
→
04/10 22:02,
1年前
, 41F
04/10 22:02, 41F
→
04/10 22:03,
1年前
, 42F
04/10 22:03, 42F
→
04/10 22:05,
1年前
, 43F
04/10 22:05, 43F
→
04/10 22:07,
1年前
, 44F
04/10 22:07, 44F
→
04/10 22:09,
1年前
, 45F
04/10 22:09, 45F
→
04/10 22:10,
1年前
, 46F
04/10 22:10, 46F
→
04/10 22:11,
1年前
, 47F
04/10 22:11, 47F
→
04/10 22:12,
1年前
, 48F
04/10 22:12, 48F
→
04/10 22:12,
1年前
, 49F
04/10 22:12, 49F
推
04/10 22:28,
1年前
, 50F
04/10 22:28, 50F
→
04/10 22:28,
1年前
, 51F
04/10 22:28, 51F
Python 近期熱門文章
PTT數位生活區 即時熱門文章