[問題] thread 吃光 CPU 效率,想自行控制
最近在做一個專案,裡面有三十個設備要控制
因為三十個設備不能互相卡住,所以就開了三十個 thread
另外程式要有所展示,所以 main thread 也就是 ui thread
由 main thread 開出 30 個設備 thread, 那現在看到有 31 個了
CPU 的效率撥進來這支程式,假設是平均且 free run
那麼每個 thread 都佔用了 1/31 的 CPU 時間
我在每個 thread 做過一次判斷後,都有 sleep(1)
這意思是每個設備,每秒都判斷一次應該要做什麼動作
這時 UI 變很卡了...
理想上每個 thread 遇到 sleep 指令,它就睡了
然後 CPU 時間會撥給剩下的 thread 去平均分享
如果每個設備的判斷式都執行很快,那麼在別的 thread 的 sleep 一秒內,就足以做完
當程式跑久以後,每個 thread 都有可能全速奔馳
只要它們別同時醒著
也許是我寫的判斷式效率不好,漸漸的感覺到程式變很卡
而把 sleep 加長有助於解決這個問題
其實我想分配每個 thread 的優先權了
一開始的想法便是利用 lock
首先產生一個 lock 當全域物件
30 個 thread 都引用它且互斥
with lock:
設備判斷式
這樣可使 30 個 thread 同時只能有一個執行
而我在設備判斷式裡,也只簡單的把一個計數器加一
據以觀察 30 個設備的執行機率平不平均
結果是非常不平均;但這樣寫是最簡單的
我會擔心,會不會運氣不好,某設備經常不執行?
後來我想了一個法子,由我自己控制
我產生一個 list, 裡面有所有 30 個設備 thread 的指標
只要某 thread 被執行,就把它移到 list 的最尾端
那麼最前端就是沒被執行到,我希望它執行的
用這個方法等於把 30 個 thread 串起來,因為同時只有一個在動
我等於就把它們變一個 thread (難道 thread group 就是在做這件事?)
我希望的是 30個設備 thread 總共佔用 50% CPU 時間
而 main thread 佔用另外的 50% CPU 時間;也就是平分~
這樣會不會有什麼問題呢?有沒有更簡單的想法呢?
覺得自己打造了不少輪子
而且這樣似乎又違反了一開始的初衷(設備不要互相卡住)
因為我只要一個設備有 bug, 卡住不動
剩下 29 個設備會全部賠葬 XD
把話反過來說好了,一開始我就不想寫 30 個 thread,我本來要用一個 thread 做完
舉例來說,一個射擊類電玩裡,敵機可能有一百架
難道這電玩就用一百個 thread 來寫?
當然是一個 thread 就很好寫啊...
但因為我的開發出了點問題,老闆就叫我快點改成 multi-thread
至少一個設備卡住也不影響其他設備
另外當初在學同步物件時,有個東西就搞不懂
好像有個叫 信號機 的物件,它不像 lock 是完全互斥
而是它可以允許一個量(比如同時有五個 thread pass,一個結束才能再喚醒一個)
我一直無法想像這東西用在哪,從沒用過
難道就用在這?
比如,我 30 個設備會卡,但當初開發到一半時還很順,大概可以容許 15 個設備
我就設 15 個設備的信號機,這樣程式也不會卡,但也不是同時所有 thread 都在動
難道就是用在這?
但我還是會擔心,允許 15 個設備,就有哪個設備不受照顧,睡死一方..
這種同步物件,有保證所有 thread 的機率儘量均等嗎?
以上請教,謝謝
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 49.216.44.32 (臺灣)
※ 文章網址: https://www.ptt.cc/bbs/Python/M.1674995478.A.744.html
另一個想法,這搞不好是 GIL 的鍋
不如把 30 個 thread 拆成 30 個 process
還是會很快
UI 則獨立出來另一支程式
不過這樣我一直沒去想 thread safe 的問題,現在就要好好想想了
(我每個設備都要去碰 RS-232, i2c 等等硬體,這下硬體大車拼了)
但暫時不想這樣玩
不然萬一真的玩光了 CPU 效率,不就影響到 OS 了...
※ 編輯: HuangJC (49.216.44.32 臺灣), 01/29/2023 20:43:07
想想雖然有 GIL,但 multi-thread 還是有用處
因為我有控制速度慢的硬體
全部三十個設備的 relay 都在 RS-232 上(其實是 485,全部連上再用modbus 定址)
當一道硬體指令發出且還沒回應時,CPU 時間就撥給其他 thread 了
我沒記錯吧?
所以如果我不用 multi-thread 寫,那麼這些零散時間就不會被利用到
當控制硬體時,因為一個設備它的 thread 仍在佔用,且我叫它們全互斥
那其實程式會跑很慢!
→
01/29 23:13,
1年前
, 1F
01/29 23:13, 1F
這太暴力了 XD,整個專案成本都會上升
目前用 RPI 3B+
推
01/29 23:18,
1年前
, 2F
01/29 23:18, 2F
→
01/29 23:19,
1年前
, 3F
01/29 23:19, 3F
因為我也只抱怨公平性 XD,但我發現做不來
比如 thread 1, 然後 thread 2, 然後 thead 3 ; 這是我手工打造的順序且全速執行
如果 thread 1 裡有個 sleep 我得自己去改寫,
因為這時它會空等,沒讓 thread 2 趁機先跑
sleep 是我自己下的,全域搜尋並改寫不難
但如果是操作低速硬體,這時多出來的 CPU 效率可以給 thread 2 先跑
這我改寫不出來;因此我有在想同時允許兩個 thread 跑
→
01/29 23:19,
1年前
, 4F
01/29 23:19, 4F
協同式多工?我想到很久前寫 windows 的 message loop 的經驗
我好像就正在手工打造 coroutines,得比比官方的輪子怎麼用
→
01/30 01:49,
1年前
, 5F
01/30 01:49, 5F
→
01/30 01:52,
1年前
, 6F
01/30 01:52, 6F
→
01/30 01:53,
1年前
, 7F
01/30 01:53, 7F
https://ithelp.ithome.com.tw/articles/10262385
看了這個才理解大家說的 task
剛還去 google task vs process, 因為我搞混了兩個詞
看來一個可執行檔跑起來,叫做 process
而一個可被執行的程式片段,叫做 task
推
01/30 09:29,
1年前
, 8F
01/30 09:29, 8F
→
01/30 09:30,
1年前
, 9F
01/30 09:30, 9F
→
01/30 09:32,
1年前
, 10F
01/30 09:32, 10F
→
01/30 09:34,
1年前
, 11F
01/30 09:34, 11F
→
01/30 09:35,
1年前
, 12F
01/30 09:35, 12F
推
01/30 09:40,
1年前
, 13F
01/30 09:40, 13F
→
01/31 06:32,
1年前
, 14F
01/31 06:32, 14F
→
01/31 06:32,
1年前
, 15F
01/31 06:32, 15F
→
01/31 06:32,
1年前
, 16F
01/31 06:32, 16F
在 C 是這樣,在 python 因為有 GIL,它的 thread 是閹割的...
我曾想 GIL 是不是一種設計上的限制,
後來發現它很大量的減少我使用 critical section,也算方便
舉例來說,一個變數如果略大,佔 8 個 byte 好了
真正的 multithread 我必需擔心當變數只更新了 4 個 byte 就被另一個 thread 參考
解決方法就是用各種同步物件去限制
但在 GIL 的閹割之下,這種事在 python 我從不擔心
CPU 當然不會更有效率,舉兩個 thread 來說,c 和 python 都是'大家平分各50%效率'
差別就在'卡卡的',比如,同時控制 x, y 軸的控制器,若 x, y 軸各用一個 thread
用 C 寫,就能完美的走斜線,用 python 寫,看網友抱怨是會抖動
現在 Coroutine, 譯為協程,看到今天感覺這東西更妙
頗有以前我寫 windows sdk 時接觸的 message loop 的感覺(事實上它生了個 loop)
我可以指定在 sleep 時才切換 cpu 到另一個 'thread'
切換時機由我指定,那我就更不擔心變數被另一個 thread 偷換掉了
我對同步的協調應該可以更少
但同樣的,一個 'thread' 卡住,就是大家都卡住,這問題又冒出來了 XD
※ 編輯: HuangJC (49.216.44.32 臺灣), 01/31/2023 06:51:57
為什麼 Coroutine 比 Multithread 有效率?我有個猜想,但不知有沒有人附議
(沒法子,我經常要去想原理,以便應用在其他地方,或用來理解 bug)
MT 需要本文切換,這是它慢的原因
而它何時切換?因為 GIL,它不會在指令執行到一半時切換。它總是執行完。
上次看到某網頁寫,它總是執行一百道 python 指令,然後切換
100 是寫在 spec 裡的或者任何一家實作 python 的廠商都可以改?我不知道
但反正它是這樣切
而 C 語言的切換是用時間,比如 100 毫秒,由 timer 發起中斷,CPU 收到中斷
配合 OS 寫好的中斷處理常式引起本文切換。因為是用時間,所以平滑多了
而一道 C 指令會譯成幾道組合語言?不清楚。每個機械碼佔用幾個機械週期?不清楚
但用 timer 由外部引起中斷,這平滑很多很多
平滑到 C 指令可能才執行一半,就被切去另一個 thread 了
Coroutine 則是本文切換由軟體工程師決定,在每個 sleep 點才切換
如果完全不寫 sleep,就會完全不切換;引起 Block XDDD
而因為切換點都在 python 肚子裡,所以也不勞動外部 timer,不勞動 OS 中斷常式
因此才能做到 Coroutine 的 'thread' 不必註冊到 OS 層級
註冊到哪級是一回事,我以為只要本文切換就是個負擔
但現在這個本文都是 python 肚子裡的事,可能會因此小點
猜是這樣猜啦...
※ 編輯: HuangJC (49.216.44.32 臺灣), 01/31/2023 07:57:50
推
01/31 23:19,
1年前
, 17F
01/31 23:19, 17F
→
01/31 23:19,
1年前
, 18F
01/31 23:19, 18F
→
01/31 23:21,
1年前
, 19F
01/31 23:21, 19F
→
01/31 23:22,
1年前
, 20F
01/31 23:22, 20F
→
01/31 23:22,
1年前
, 21F
01/31 23:22, 21F
→
02/01 00:44,
1年前
, 22F
02/01 00:44, 22F
有空我檢查一下,我不記得我 main thread 有用 lock
應該只是 31 個 thread 真的太多,拖慢了
如果用了 coroutine, 那真是把 30 個變成 task,共享一個 thread
我目前做法是觀察會變卡的東西,最明顯的就是 mainthread 每秒更新的時鐘(hh:mm:ss)
它變成兩秒跳一次,於是我把何時更新其值,設一個 watch dog
如果其值用跳的,我就開動 lock
這個 lock 就是用來鎖住 30 個設備的 thread
但目前這樣做也不會順就是了
推
02/01 00:52,
1年前
, 23F
02/01 00:52, 23F
※ 編輯: HuangJC (49.217.70.24 臺灣), 02/01/2023 09:10:48
各位太熱情了,目前我著迷在 Coroutine 了
我相信它可以解決我的問題。。。
學每個東西都需要時間,我要先決策要不要去學,然後才學
而不是學了才來決策。。。。
(我怎麼聞到自己不求上進的 fu)
不是啦,我不只是工程師,我也要想自己的成本問題啊
當我能力不足,先決策再投資生命並沒有錯!
我會先 google 一下大家提的東西,但我未必學得會,未必投資;對吧 :P
→
02/01 16:38,
1年前
, 24F
02/01 16:38, 24F
→
02/01 20:10,
1年前
, 25F
02/01 20:10, 25F
※ 編輯: HuangJC (123.204.157.162 臺灣), 02/03/2023 12:47:28
推
02/04 00:17,
1年前
, 26F
02/04 00:17, 26F
Python 近期熱門文章
PTT數位生活區 即時熱門文章