環境表格

lua-users home
wiki

Lua 5.0

在 Lua 5.0.2 中,Lua 閉包(不含 C 閉包)具有環境表格。環境表格用於查詢未繫結名稱(即「全域變數」)。在執行函數表達式或陳述式來建立新閉包時,新的閉包會擷取目前執行的閉包環境表格,因此在未變更的情況下,所有閉包會共用同一環境表格,這讓它在很大程度上成為與前一版本 Lua 中相同的全域表格。

閉包的環境表格可以在 C 中使用 lua_getfenv() 存取,在 Lua 中使用 getfenv() 存取;它可用 lua_setfenv() / setfenv() 設定為不同的表格。請參閱這些函數的手冊。

在 5.0.2 中,C 閉包實際上共用一個全域表格,此表格會與目前執行的執行緒關聯。C 閉包可以使用偽索引 LUA_GLOBALSINDEX 來使用此表格(實際上可以用 lua_replace(L, LUA_GLOBALSINDEX) 修改此表格)。這實用但也可能會有些混亂。

Lua 5.1

在 5.1 中,每個 C 閉包都有自己的環境表格參考項。但是,偽索引 LUA_GLOBALSINDEX 未變更,因此參照 LUA_GLOBALSINDEX 的既有程式碼會繼續述及目前執行的執行緒「全域」表格。(該表格現在稱為執行緒的環境表格,而且可以透過對執行緒物件本身套用 lua_getfenvlua_setfenv 來存取和修改。)如果要存取閉包環境表格,請使用 LUA_ENVIRONINDEX,可以透過對閉包物件套用 lua_getfenv()lua_setfenv() 來存取和修改閉包的環境表格(不論是 C 閉包或 Lua 閉包)。

您可以用 lua_replace(L, LUA_ENVIRONINDEX) 取代「您自己的」環境,就像您可以用 lua_replace(L, LUA_GLOBALSINDEX) 變更全域表格。但是,更常見的情境是對新建立的閉包呼叫 lua_setfenv()

(完整)使用者資料也具有環境表格,儘管我在其他地方主張(UserDataRefinement),這個名稱令人混淆。使用者資料環境表格是儲存與使用者資料相關資訊的地方,沒有對應的偽索引。

新建立的閉包(以及使用者資料)會從目前執行的閉包取得其環境表格(如果有的話),否則從目前執行緒取得。(再次重申,正如我在其他地方主張的,這對使用者資料來說並非具有特別實用的預設值,但我不會再深入討論。)

何時何地擺置什麼?

C 閉包並未真正有繫結和未繫結變數的概念。但是,C 閉包有一些放置 Lua 值的地方

要找出這些選項中哪個適合用在哪裡可能頗具挑戰,但我提供我自己的非常個人化指南

使用 upvalue

使用閉包自己的環境表

使用執行緒的環境表

使用記錄

執行緒安全性的注意事項

唯一執行緒安全的選項是堆疊和執行緒的環境表。如果 lua_locklua_unlock 已適當定義,儲存至記錄或閉包的 upvalue 陣列或環境表不會損壞 Lua 狀態,但沒有任何東西可以阻止其他(作業系統)執行緒同時儲存一些其他的東西,導致標準競爭條件問題,其中存取與更新會被另一個程序中斷。特別適用於 lauxlib 實作的參考(如果儲存在記錄中)。然而,有了現在所有不同的環境表,這應該更容易避免了。

如果每個 Lua 執行緒都對應到單一作業系統執行緒(可以是一對多對應),則執行緒的環境表為執行緒安全(可能是一對多對應);亦即,指定的 Lua 執行緒總是在同一個作業系統執行緒中執行。這樣便使其成為維持 lauxlib 風格參考的誘人選項,若無法完全避免使用它們。

在相同條件下,閉包的 upvalue 陣列和環境表執行緒安全,但這通常難以保證。對於短暫生命期的閉包可能是情況如此,例如返回的用於實作迭代器的閉包,但對於長生命期的閉包而言不太可能是情況如此,例如函式庫函數。

如果您發現自己正在多執行緒環境中修改註冊碼,您應該仔細考慮使用某些形式的鎖來保護修改。這也適用於使用基於註冊碼 lauxlib 的特點(例如 luaL_newmetatable()),如果它的使用沒有被限制在執行緒初始化前的階段。


最近變更 · 偏好
編輯 · 歷史
最後於 2005 年 12 月 22 日星期四凌晨 2:18 GMT 編輯 (diff)