Re: [問題] 請問 Coroutine & 一般 callback 合作的問題
※ 引述《zerof (貓橘毛發呆雕像)》之銘言:
: : 我也可以用 multi-process 而不是 thread 來解啊
: : 解法不只一種,而且我不認為問題出在 GIL
: : 畢竟我的 thread 有三十多個,我也真嫌多了
: : 用了 Coroutine 就合併回一個,但卻是三十多個 task
: : 我覺得這也蠻好的
: : Coroutine 既然是個潮流就來了解一下,有別的解法就先放一邊吧..
: : 真要 C 我何不回 C 的世界,寫純 C..
: 你要不要看看 Chrome 開起來的時候平常是起幾個 Threads ?
在你這篇文前我就想過這個問題了
可是這個比較並不公平
你基於 chrome 跑起來流暢,偷渡了 python 即使只寫 single thread
執行速度都輸給 c 的事實
一個是 compile 語言,一個是 script
是否 chrome 也用 python 寫,但用上 pypy 取消 gil 的影響再來比?
有一件事我沒在前面的文章中提及,就是 multi-thread 在 debug 時給我的困擾
我用中斷點暫停了一個 thread, 但其他 29 個 thread 還在跑
這導致大家共用的變數飛快的改變著,我很難單步執行
(但的確又有一些 thread 在背景還能跑也給了我方便)
而 Coroutine 它們事實上是同一個 thread
這件事讓我可以在中斷一個 task 時,其他 task 都停下來
這是我要的;之前沒 Coroutine 我也漸漸在安排這種架構
比如我有十個 sensor, 本來各跑一個 thread,後來串起來在同一個 thread 跑
比如本來一秒讀一次,我就一秒啟動一個新的 timer thread
結果我中斷在某個 sensor read 時,
還每一秒都有一個新的 read request 闖進來,不堪其擾了
所以雖然讀 sensor 是飛快的事,一秒內一定能讀完,可以一秒啟動一個 timer thread
我還是只啟動一個 thread 並且寫成 while loop
這樣只要讀取沒完成,就不會有新的 read request
可以說 free run 並沒有問題,是 debug 時出一堆問題
就我看目前我的程式都不要改,卡的情況老闆也能接受
不接受的是我,因為只要 debug, 程式就卡得不能動
當然短期內我不用單步執行,狂加 log 也解了不少問題
: Coroutine 是潮流? 你不是寫 C 嗎怎麼沒用過 libtask ?
真沒用過
: 這也難怪前面被嗆先回去讀 OS , Preemptive/Cooperative multitasking 是不是
: 都沒聽過 ?
這就有聽過了,寫過 windows sdk, message loop 就類似協程的一種
guizero,或者說一堆 GUI framework, 都用 call back 或 event driven 運作
就算這名詞沒聽過,也許我已經在寫這種程式了
所以其實我本來想自己打造 Coroutine 的
就我看,task 其實都是在同一個 thread 跑
但是有一個 framework 巧妙的把它轉換成 message loop 的型式
只要目前執行中的 task 阻塞,就跳轉另一個 task 跑
: 如果問題不是出在 GIL ,那問題是出在你的 code 架構不對?
如果我對 Coroutine 的理解沒錯,也就是它只是幫我實現我想要的架構
我完全可以不用 Coroutine,但又只用一個 thread 寫出來
所以,是的:我的 code 架構不對
我常說的一件事:如果寫一個射擊電玩,畫面上有五十架敵機
哪有必要用 50 個 thread 去寫?當然一個 thread 運作五十架敵機沒有問題
但因為我在安排架構上出了問題,所以才改用 thread 寫
在 thread 內我可以專注在運算一架飛機
寫著寫著想要 sleep 就sleep, 不用擔心其他飛機被暫停了;反正它們在其他 thread
看在事實上只有一顆 CPU,只有一個核心的份上
我認為所有的 multi-thread 其實都可以寫成 single thread
用上 multi-thread 不就是因為思維可以清晰,有底層硬體支援
所以我的邏輯概念可以拋掉一些事由 OS 負責嗎?
Coroutine 也給我這種感覺
現在我可以想 sleep 就 sleep, 控制權會交給其他 task
而我的程式語法還是單純
我本來要自己硬幹的,若我幹出來了,我就會說那個架構好,而現在這個架構差
方法就是打造自己的 message loop,把一份工作切成許多程序薄片
每一個薄片做完就切換到另一個薄片,每個薄片間沒有 multi-thread 會發生的交鏈
這樣就算沒 GIL,我也是自己手工打造了一樣的副作用
所以我不會抱怨 GIL
: 我確實是蠻好奇的啦,如果你可以斷定不是出在 GIL ,難道 PyQT 的 QThread 是寫
: 來好看的?
話不能這麼說,常說的一句話:問題有很多解法
別人依賴我不依賴啊..
PyQT 把這架構,打造給依賴它的人
(區塊變色好難,好像只變到一行?)
: 我想你從頭到尾沒有搞清楚 GIL 所以根本不明白 GUI 為什麼會 freezing
: threading 本身就是 preemptive 的, GIL threads 在 context switching 的時候
: 會有 Locking 的問題,反過來說, GUI 要執行的 main thread 無法保證總是搶得到
: GIL ,而產生 freeze 的現象;當你 threads 開得多,裡面又都是跑 python code
: 的時候就會非常的明顯。
: Python 連 serial/smbus 的 libraries 都是 blocking I/O ,最底層摸到 sockets
: 的時候的確是會 release GIL ,但回過頭來就還是 preemptive 的本質:你沒辦法保
: 證 GUI 用的 thread 一定會搶到 GIL 。
這些我知道
: 這問題換到用 asyncio 並不會被解決,本質上的差別。 asycnio 底層用的 sockets
: 是 non-blocking 的,你用同樣的 libraries 來跑,一樣會遇到 GIL 的問題,而且
: 更嚴重。(GUI 一個 loop, asyncio 一個 loop)
用 29 個 working thread 和一個 ui/main thread 來說
我目前的架構是 free run 時,每個 thread 佔用三十分之一的 process 效率
UI 必需和大家共享,那也才三十分之一, 3%
但我不很在乎我的 working thread, 願意合併成一個 thread,內含 29 個 task
於是 main thread 變成 二分之一,50% 的效率
(而我不清楚 main thread 會不會加重,開出的其他 thread 不與它平分
但若加重也是這兩個比較的 main thread 都加重)
當然事實上可能不是大家都 free run
寫 multithread 時 free run 會很恐怖吧?我都會加 sleep
而根據 sleep 的時間,如果某 thread sleep 特長,長到奪得控制權的機率並非平均
上述算式就差更多;但基本上我狠狠的加大 working thread 的 sleep 時間並不是問題
: loop.run_in_executor 的本質是 concurrent.futures 底下的 ThreadPoolExecutor
: ,當然你也可以改用 ProcessPoolExecutor ,這跟你原本的 threads 改成 Process
: 一樣,唯一的差別是 Executor 用的是 Worker model。
: 回過頭來說,照你原本 30 threads 的版本,可以先試著用 pypy 跑看看能不能加速
: Python code 執行的速度,減輕 GIL 的影響 (沒錯, pypy 也是有 GIL);另外的選
: 項用 Cython 把 threads 用 release_gil 加速。
: 上面兩個選項都是最小改動,要完全避免 GUI freezing 的話就是把 threads 移到
: 另一個 Process,變成 Thread(GUI) <-> Process (Threads) 的架構,但如果你在
: 各個 threads 之間有 sharing data , IPC 的成本不見得會比較低。
1. 取消 GIL 後,我的程式就要面臨 thread safe 的挑戰;目前都沒安排
GIL 為什麼被採用?當我寫了一陣子 python 後我發覺,它不像 C 是要高效的
它釋放我的腦袋在處理邏輯上,當我緊張的寫一小段程式,要看它會不會當機時
C 的寫法會在 thread 裡撞變數,一定要 critical section
而 python 的寫法不會,可以說 python 的 ATOM 就是很大顆,蠻橫但好用
2. 切成兩個 Process 是我前面也談過的架構, IPC 是一定要的,比如跨網路
由遠端網頁連入近端,那麼近端只要運作所有 working thread,根本不用 GUI
這部份完成了啊,我的 UI 不讓 working thread 直接取值的
UI 只寫入 MySQL, working thread 只從 MySQL 取資料就開始跑
所以我只要運作起 MySQL, 它就替我做完我要的 IPC 了...
開放網路連入 MySQL server,就變遠端控制..
來說說 GIL 我看到的經典差別是什麼
以兩軸馬達控制器來說,C 寫的可以完美的走一條斜線
而 python 寫的,則是會抖動的走一條斜線;要嘛走 x 軸,要嘛走 y 軸
就是不同時走
當然用點技巧解掉就不會了,總是得刻意用最簡單的寫法來強調 GIL 的影響
我這段反白,和你上面那段紅色的,差別是一個用專有名詞,一個用口語描述
如果你說我這樣叫做不懂,那差別在哪裡?
(差別在 google 搜尋方案時,有專有名詞比較精準;當然這是好事)
不過我還要說一個我面臨的問題,不是 GIL 的影響
而是 GUI framework 一般來說,有一個 'render' 的時間
如果我讓程式進入一個自己的 while loop 永遠跑不完
在 working thread 是沒問題的,但在 UI thread 它就是不繪圖
可以說我的繪圖指令,或者擺放各個 UI 控制項的指令
都只像在安排結構,而這個結構必需等 ui thread 有空時才會被 guizero 解析並繪圖
因此繪圖的反應要快,那每個 call back 就要快點執行完且 return 回 main loop
如果執行快但很快又要執行新的 call back, 留給 framework 的 idle time 不夠長
它就是不繪圖;症狀是我可以用 print 或 log 看到,指令都有執行完但它就是不繪
如果在 windows os, 我會下一道 update window
相對其他類似的 framework,我也都在找這道 update window 指令
要嘛沒找到,要嘛我覺得快點執行完返回 main loop 也不錯,我已經習慣了
GIL 不只要搶到,還要 idle 夠久,否則不只是卡,是畫面根本不出來!
: 當然你也可以寫 C 啦,能用的東西多得是,會不會而已。
: BTW , asyncio 真的不好嗎?當然不是。但 Python 的 async 有傳染性,而且受限於
: GIL ,sync -> async 不實際, async -> sync 更是自找麻煩。
: 糯米摻黏米又怎麼會好呢? 要寫 GUI 不如左轉 nodejs 吧
你提 nodejs 就更可以狠狠的罵我了
不會,完全不會 XD
當年寫 C 時,我是計較千分之一秒的人
現在寫 python,為什麼不堅持 C?
因為一開始專案評估時,老闆就對我說:
我們的 sensor, relay, 你一分鐘運算一次就好
我一聽,這根本是殺雞用牛刀,python 慢歸慢,夠用;來學一下 XD
所以比如切兩個 process 可解,我為何要用 Coroutine 解?
因為它有趣,我要求不高,還可以解
事實上現在我還是寫成 sensor/relay 一秒運算一次
因為如果一分鐘運算一次,我在 debug 時也等得累了
http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
傳染性是指這件事嗎?
我想這就是大力向我推薦 Coroutine 的朋友該替我想一下的了
當然我知道大家都不是各領域的業務,只是興趣
他喜歡 Coroutine,想讓我試試 Coroutine
那我試了,碰到問題並回報
如果他不幫我解決這問題,那我以後就不會再考慮 Coroutine
難道大家要接受 Coroutine 是個雞肋這想法?
既然說出要安排個好的架構(但好的架構就是排除 Coroutine?)
好啊,那這就是個題目
如果我成功的切成兩大塊,那就不會有很多的混 call
兩大塊間用 async/sync queue 連來連去
就好像程式內的 IPC,這是 async 和 sync 裡的 IPC
那我就真的能用它解決一些問題
而那些問題我未必有描述在文章裡,所以你不知道我有這些要面對
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 1.168.18.180 (臺灣)
※ 文章網址: https://www.ptt.cc/bbs/Python/M.1675837098.A.C3C.html
推
02/08 15:46,
1年前
, 1F
02/08 15:46, 1F
他的文我一開始也沒看懂,後來才發現都我知道的概念
只是我沒用那些字眼
即使都是中文,語境也是要吸收的
※ 編輯: HuangJC (1.168.18.180 臺灣), 02/08/2023 15:58:34
→
02/08 16:28,
1年前
, 2F
02/08 16:28, 2F
→
02/08 16:28,
1年前
, 3F
02/08 16:28, 3F
→
02/08 16:30,
1年前
, 4F
02/08 16:30, 4F
可是他的文我也覺得講到我本來就懂的部份啊
是他以為我不懂。。
那就真的解掉問題嗎?
舉 chrome 為例子,那又不是沒 GIL 版本的 python
這樣做比較,似是而非吧..
偷渡了論點你能比較出來嗎?
在做物理實驗時,觀測變數必需獨立
而不是利用改善變數 A,去證實改善變數 B 有效吧?
就好像台灣近年來愈來愈差
於是有人說:二十年前還有聯考,改回聯考台灣就能重返榮耀
是這樣證明的嗎?
怎麼不說時代變了,從前的教育也會不適用
多變數能這樣觀測及證明?
但是普設大學很多人說不好,改回聯考的論點也讓人秒懂
讓你秒懂的東西就是對的?
→
02/08 17:30,
1年前
, 5F
02/08 17:30, 5F
→
02/08 17:30,
1年前
, 6F
02/08 17:30, 6F
我上次說我 C 的底子十年對吧..
好吧,我比你資深,沒錯
但我也說了,這時膨風沒有好處
反而只會被電:都這麼資深了這個還不會
→
02/08 17:53,
1年前
, 7F
02/08 17:53, 7F
謝謝,因為我英文很爛 XD;等等修
→
02/08 21:26,
1年前
, 8F
02/08 21:26, 8F
→
02/08 21:26,
1年前
, 9F
02/08 21:26, 9F
→
02/08 21:26,
1年前
, 10F
02/08 21:26, 10F
那你也看看介紹給我的網友怎麼說的
coroutine 的代價比 multi-thread 低...
光論邏輯其實都是把程式切薄片
但實作方法就有差別,也就是這個差別造成它的代價低
那現在你要怎麼給評估建議?
'凡是不能寫成純 Coroutine 的,就不建議用 Coroutine'?
倒也不是,前面有板友給我解了,有本事 thread 和 task 混在一起
你也說了,使用 UI framework, 有 callback 是常態
我以為這句的語氣不應是對我,應該是對別人
因為我本來就知道是常態,我一直說我要走通 sync call async 這條路,無法迴避
是別人說我為何把問題搞得這麼複雜
那這時接你這句'使用 UI framework, 有 callback 是常態'不是很能替我說話嗎?
這是我無法避免的挑戰,不是我把問題搞複雜
先不說切成兩個 process 的解法
我自己就有提出,你也提了一次,這沒什麼爭議的
如果你附議'當有 UI 時還寫 coroutine 是自找麻煩'
那這件事就落幕了,當初我說我想自己分配 CPU 時間,網友的建議...
因為他們不知道我有 UI 要寫,所以不能說建議錯
但如果先知道我有 UI,有 sync call back
是否他們根本就不做此建議?
我知道大家只是建議,我自己的工作是自己的責任
只是現在別管怎麼寫了,連'評估'都成為一道題目
所以大家要附議這句嗎:當有 UI framework 時,用 coroutine 是自找麻煩,不值得
(因為我真的可以用兩個 process 寫,而且也鋪陳很久了)
》果然回文是浪費時間
你的態度認為你是對的,是來教訓我而我不受教
所以才會得到浪費時間的結論
那就請你不要回文了,因為如你說的,我自己可以去看那薄薄的文件
我還沒給你錢呢.. 算一下你的時薪,如果要聘你當顧問,我一定出不起
我以為這裡是討論的地方,不是教訓的地方
我之所以不敢亮出自己真實年資,是因為我的實力根本不能彰顯年資
要教訓別人談不上,都是來討論的
→
02/08 21:35,
1年前
, 11F
02/08 21:35, 11F
→
02/08 21:35,
1年前
, 12F
02/08 21:35, 12F
→
02/08 21:35,
1年前
, 13F
02/08 21:35, 13F
→
02/08 21:35,
1年前
, 14F
02/08 21:35, 14F
→
02/08 21:36,
1年前
, 15F
02/08 21:36, 15F
→
02/08 21:36,
1年前
, 16F
02/08 21:36, 16F
我的理解是這樣的
比如一個 thread 說
a = 0102030405060708h (簡單的八個 byte 長度變數)
用硬體觸發 CPU 中斷,則很可能在 a = 01020304h (前四個 byte)
已經存入變數位置時,失去 thread 控制權
(當然這看 compiler 怎麼實作,通常一道機械指令就搬完八個 byte,就不會發生這事
我要討論這個時必需假設實作了一個很爛的 compiler)
但下次 thread 取回控制權時,還得接著存入後面四個 byte,也就是 05060708h
為什麼會知道從這裡繼續存?這就是 context switch 保留了一切的緣故
其中包含了所有的暫存器
就邏輯概念來說,除了 thread 失去控制權幾微秒,其他事一如往常
所以這個 thread 可以無痛的繼續跑下去
既然這麼無痛,那任何時間切都可以,用的是時間中斷
python 的就不是了,比如一次執行一百道 python 指令
所以我說 python 的原子又大又蠻橫,變數 a 的 8個 byte 我是沒在擔心被拆開的
以這樣的程式(虛擬碼)來說
thread1 = while True
print('t1 looping')
thread2 = while True
print('t2 looping')
在 multi-thread 可以正常跑
但在 multk-task,就會出事了
task1 = while True
print('t1 looping')
task2 = while True
print('t2 looping')
因為若 task1 先執行,它就會 hold 住控制權,再也不釋出
至少也得在裡面擺個 asyncio.sleep(0)
那這道指令其實就是切程式薄片的位置
它不是時間引發中斷
》更需要理解 time sliding
它是由工程師自己用 code 決定要在哪裡保留機會切換控制權
我得去協調這些,但如果我有需求,我可以讓切換很少發生;夠用就好
比如我說我們專案是一分鐘執行一次就好
那我放心給每一個 thread 執行兩秒鐘
三十個 thread 都要輪到,輪一次就一分鐘
這也是可以的
不過呢,我的程式不快,相反的是很慢
比如某個動作一做就是這樣
開窗戶 =
relay On
sleep(七分鐘)
relay Off
我要安排的是高達七分鐘的工作 XD
看來看去 Coroutine 很方便,直接塞三行指令完成
如果自己做又不想塞住,那我只好把工作排上時間,丟進 message queue 裡去了
這像你說的要了解更多 time slide?
我所看到的文件是說,要了解 message loop
而且我是從 windows sdk, windows MFC framework 一路學上來的人
接觸了微軟那套 message loop 很久,的確有熟悉的感覺
比如使用者按了鍵盤,就有 onKey event, 滑了滑鼠,有 onMouseMove event
如果我 onKey 處理十秒,那滑鼠也會有被卡住十秒的感覺
onkey 就是得快快處理完
那麼這套 framework 就能讓 user 感覺鍵盤和滑鼠兩件事,像多工一樣
但它們事實上不是 timer 中斷直接觸發 CPU 引起全部暫存器的本文切換
而是一段段完全不會同時執行的 event driven 程式片段
我這段理解,有誤嗎?
》複雜度問題才會很多程式都還是用 multi threads
好啊,不談怎麼寫,只談決策
所以 Coroutine 到底為什麼要發明出來?
1。不好學
2。學了,也不好用
如果你是對的,那你就是在教訓別人,當然你覺得是浪費時間
如果有任何一位大德跳出來說'其實很好用,就算是 UI framework 也很好用'
那這就是討論了,相信對你也就不算浪費時間了
※ 編輯: HuangJC (123.204.157.162 臺灣), 02/08/2023 23:14:59
※ 編輯: HuangJC (123.204.157.162 臺灣), 02/08/2023 23:17:29
※ 編輯: HuangJC (123.204.157.162 臺灣), 02/08/2023 23:22:07
→
02/09 10:38,
1年前
, 17F
02/09 10:38, 17F
→
02/09 10:42,
1年前
, 18F
02/09 10:42, 18F
→
02/09 18:18,
1年前
, 19F
02/09 18:18, 19F
→
02/10 00:20,
1年前
, 20F
02/10 00:20, 20F
→
02/10 00:20,
1年前
, 21F
02/10 00:20, 21F
討論串 (同標題文章)
本文引述了以下文章的的內容:
完整討論串 (本文為第 4 之 4 篇):
Python 近期熱門文章
PTT數位生活區 即時熱門文章