Python - Fatal IO error 11 (Resource temporarily unavailable) on X server
日前在設計一個使用 GTK 的 Python 程式時,碰到了 Fatal IO error 11 (Resource temporarily unavailable) on X server
的錯誤。
這問題不算希罕,在下列文章中就討論了原因。
- XIO: Fatal IO error 11 (Resource temporarily unavailable) on X server
- python multiprocessing with 2 gtk windows
我的結論是,此為 Python gtk 模組和 multiprocessing 模組搭配使用時的潛在 bug 。
首先看看會引發 Fatal IO error 11
的設計方式。
在上面的設計中,只要第一個子行程結束了,那麼隨後任何操作 GTK 個體的動作就會觸發 Fatal IO error 11 錯誤。原因牽涉到 X Window 的系統架構與 Python gtk 模組的設計。
X Window 架構實際上是 client/server 架構,在螢幕上看到的任何 GUI 元件,都是由 X server 負責管理。而我們的 GTK 程式便是 X client ,它必須開啟和 X server 互動的 IPC socket 管線,再利用這個管線讀寫 GUI 元件的操作指令。 Python 的 gtk 模組,在引入時就會順帶開啟這個 X server socket 。
進一步,當我們使用 multiprocessing 模組建立子行程時,multiprocessing 用 fork() 複製了一個新的行程內容,又 gtk 函數的內部設計是以共用的方式開啟 X server socket ,故連帶著讓子行程繼承了原先開啟的 X server socket 。此時父子行程實際上共用同一份 X server socket 。
最後,當子行程結束時,會關閉它和父行程共用的 X server socket 。 使父行程或接下來新建的子行程都失去可用的 X server socket 。 於是接下來的 GTK 操作就引發 Fatal IO Error 11 on X server 錯誤。
解決方法
以 Python gtk 的設計方式來看,解答關鍵點就引入 gtk 模組的時機。如下列所示:
主行程不要 import gtk 。
改在子行程的初始程式碼中加上 import gtk
,讓每個子行程配置自己的 GTK 內容。
如此一來,每個子行程活動空間就都有獨立的 X server socket ,而不是父子行程共用。
就不會發生其中一個子行程結束,導致其他親屬行程一併失去 X server socket 的錯誤。
註: Python 使用 gtk 模組時,若有多工作業的需求,建議搭配 gtk 自帶的 gthread 機制,或者用 Python 的 threading 模組。 multiprocessing 模組為最後一招。傳統上, Multi-processing 模式的設計難度就比 Multi-threading 模式高。