探究如何整合 GLib Main Event Loop 和 Node.js 的 libuv
在普通情況下,整合 GLib Main Event Loop 和 libuv 不是件平常人會做的事,因為,一般人使用著 GTK+、Clutter、DBus 等等函式庫(Library)時,永遠只會使用 GLib 而不會使用到第二套事件引擎。但是,在 Node.js 中,其事件引擎並不是 GLib,而是使用自己的『libuv』,想同時運行兩套事件引擎是不可能的,所以這將注定我們無法以 Node.js 去引入 GTK+、Clutter、DBus 等函式庫來使用。不過,天下文章一大抄,世界上所有事件引擎的設計差異不大,在理解 GLib Context 的運作後,我們還是可以嘗試將兩者整合在一起,協同運行。 簡單理解一下事件引擎,其說白了就是一個跑到天荒地老的無窮迴圈,不停的去檢查是否有事件被喚醒。所以,由此可知,兩套事件引擎不能被同時跑起來,因為任何一個事件處理的無窮迴圈,都將導致另一個事件處理的迴圈無法正常運作。所以,首要解決的課題,就是讓兩個無窮迴圈可以同時運作。 為了解決這樣的問題,你可能會想到去使用 Thread (執行緒/線程),只要在建立兩個 Thread,然後各自跑不同的事件引擎就可以了。但是這樣做,問題馬上就會出現。由於程式正在運作的過程中,事件數量相當多,事件被安插和喚醒的次數非常頻繁,這將導致兩個 Thread 之間很難維持穩定的資料交換,你得實作 Mutex Lock 等機制來達成 Thread-safe,更還要想辦法解決兩邊各類大小資料交換的需求。種種原因,都會造成兩個事件引擎大量的彼此等待,而效能不彰。此外,也不易同時控制兩邊的事件觸發順序,以及事件被喚醒時間的精確度,如計時器(Timer)。 既然使用 Thread 是不好的做法,是否有其他方式可以解決我們一開始的課題?解決方法是有的,從事件引擎的運作細節,我們可以發現一些端倪。 一般來說,大部份的事件引擎都包括了幾個部份: 從 Initial 出發, Event Loop 的迴圈每運行一次,都經歷了『prepared』、『Polling』和『Dispatching』三種狀態,若用白話來說,他們所代表的意義就是『已準備好事件清單』、『監控事件』、『喚醒、分配並處理事件』。 如果你去參考事件引擎程式碼,會發現在三個狀態之間,分別有『prepare()』、『query(...