Lua中的線程和狀態

1、概述
    線程(thread)作爲Lua中一種基本的數據類型,它代表獨立的執行線程(independent threads of execution),線程類型是實現協程(coroutines)的基礎,注意這裏的線程類型不要與操作系統線程混淆,Lua的線程類型是Lua虛擬機實現一種數據類型。
從Lua腳本來看,一個協程就是一個線程類型,比如:
  1. local co = coroutine.create(function() print("hi") end)  
  2. print(co)  --output: thread: 0038BEE0  
    準確來說,協程是一個線程外加一套良好的操作接口。
從實現角度來看,一個線程類型數據就是一個Lua與C交互的棧,每個棧包含函數調用鏈和數據棧,還有獨立的調試鉤子和錯誤信息。關於Lua與C交互的棧的可以參考之前寫的一篇文章。線程類型數據與table數據類型類似,它也是需要GC來管理的。
    當調用Lua C API中的大多數函數時,這些函數都是在特定的棧(或線程)上,因此在很多API中,第一個參數是lua_State*(線程類型對應的數據結構就是lua_State,或者說交互的棧就是用結構體lua_State來實現的),這個參數不僅表示了一個Lua狀態,還表示一個記錄在該狀態中的線程。注意這裏的狀態,對一個虛擬機來說只有一個,而一個虛擬機可以包括多個線程(或者說交互棧)。這個狀態也就是虛擬機的全局狀態,我們可以通過調用函數luaL_newstate()來創建一個虛擬機的狀態,該函數聲明如下:
  1. lua_State *lua_newstate (lua_Alloc f, void *ud);  
沒錯,在應用程序(比如用C++編寫的)中,爲了加載和執行Lua腳本,首先要調用的函數就是這個函數來初始化虛擬機。該函數在初始化虛擬機狀態的同時,還是創建整個虛擬機的第一個線程(稱爲主線程),或者說是第一個交互棧。爲了在已經初始化的全局狀態中創建一個新的線程(或交互棧)可以調用函數lua_newthread,該函數聲明如下:

  1. lua_State *lua_newthread (lua_State *L);  
創建一個線程就擁有一個獨立的執行棧了,但是它與其線程共用虛擬機的全局狀態。Lua中沒有函數去close或destroy 一個線程,創建的線程類型數據與其他GC對象一樣,由虛擬機來管理銷燬。

    總之,一個Lua虛擬機只有一個全局的狀態,但可以包含多個執行環境(或者說多個線程、交互棧,從腳本角度來說,也可以說是多個協程),也就是說多個執行環境共享一個全局狀態。如下圖所示:


下面將通過Lua 5.2.1的源碼來看全局狀態的數據結構global_State和腳本執行的相關的上下文環境結構lua_State,以及函數lua_newstate和lua_newthread的實現。


2、源碼實現
    首先來分析全局狀態的結構體global_State的代碼(lstate.h):

  1. 109 /*                                                                                                  
  2. 110 ** `global state', shared by all threads of this state                                              
  3. 111 */                                                                                                   
  4. 112 typedef struct global_State {                                                                        
  5. 113   lua_Alloc frealloc;  /* function to reallocate memory */                                           
  6. 114   void *ud;         /* auxiliary data to `frealloc' */                                               
  7. 115   lu_mem totalbytes;  /* number of bytes currently allocated - GCdebt */                             
  8. 116   l_mem GCdebt;  /* bytes allocated not yet compensated by the collector */                          
  9. 117   lu_mem GCmemtrav;  /* memory traversed by the GC */                                                
  10. 118   lu_mem GCestimate;  /* an estimate of the non-garbage memory in use */                             
  11. 119   stringtable strt;  /* hash table for strings */                                                    
  12. 120   TValue l_registry;                                                                                 
  13. 121   unsigned int seed;  /* randomized seed for hashes */                                               
  14. 122   lu_byte currentwhite;                                                                              
  15. 123   lu_byte gcstate;  /* state of garbage collector */                                                 
  16. 124   lu_byte gckind;  /* kind of GC running */                                                          
  17. 125   lu_byte gcrunning;  /* true if GC is running */                                                    
  18. 126   int sweepstrgc;  /* position of sweep in `strt' */                                                 
  19. 127   GCObject *allgc;  /* list of all collectable objects */                                            
  20. 128   GCObject *finobj;  /* list of collectable objects with finalizers */                               
  21. 129   GCObject **sweepgc;  /* current position of sweep in list 'allgc' */                               
  22. 130   GCObject **sweepfin;  /* current position of sweep in list 'finobj' */                             
  23. 131   GCObject *gray;  /* list of gray objects */                                                        
  24. 132   GCObject *grayagain;  /* list of objects to be traversed atomically */                             
  25. 133   GCObject *weak;  /* list of tables with weak values */                                             
  26. 134   GCObject *ephemeron;  /* list of ephemeron tables (weak keys) */                                   
  27. 135   GCObject *allweak;  /* list of all-weak tables */                                                  
  28. 136   GCObject *tobefnz;  /* list of userdata to be GC */                                                
  29. 137   UpVal uvhead;  /* head of double-linked list of all open upvalues */                               
  30. 138   Mbuffer buff;  /* temporary buffer for string concatenation */                                     
  31. 139   int gcpause;  /* size of pause between successive GCs */  
  32. 140   int gcmajorinc;  /* how much to wait for a major GC (only in gen. mode) */  
  33. 141   int gcstepmul;  /* GC `granularity' */  
  34. 142   lua_CFunction panic;  /* to be called in unprotected errors */  
  35. 143   struct lua_State *mainthread;  
  36. 144   const lua_Number *version;  /* pointer to version number */  
  37. 145   TString *memerrmsg;  /* memory-error message */  
  38. 146   TString *tmname[TM_N];  /* array with tag-method names */  
  39. 147   struct Table *mt[LUA_NUMTAGS];  /* metatables for basic types */  
  40. 148 } global_State;  
一個Lua虛擬機只有一個全局的global_State,在調用lua_newstate時候,會創建和初始化這個全局結構,這個全局結構管理着lua中全局唯一的信息, 主要包括以下信息:
    lua_Alloc frealloc:虛擬機內存分配策略,可以在調用lua_newstate時指定參數,修改該策略,或者調用luaL_newstate函數使用默認的內存分配策略。也可以通過函數               lua_setallocf:來設置內存分配策略。
    stringtable strt:全局的字符串哈希表,即保存那些短字符串,使得整個虛擬機中短字符串只有一份實例。
    TValue l_registry:保存全局的註冊表,註冊表就是一個全局的table(即整個虛擬機中只有一個註冊表),它只能被C代碼訪問,通常,它用來保存那些需要在幾個模塊中共享的數據。比如通過luaL_newmetatable創建的元表就是放在全局的註冊表中。
    lua_CFunction panic:但出現無包含錯誤(unprotected errors)時,會調用這個函數。這個函數可以通過lua_atpanic來修改。
    UpVal uvhead:指向保存所有open upvalues雙向鏈表的頭部。
    struct Table *mt[LUA_NUMTAGS]:保存基本類型的元表,注意table和userdata都有自己的元表。
    struct lua_State *mainthread:指向主lua_State,或者說是主線程、主執行棧。Lua虛擬機在調用函數lua_newstate初始化全局狀態global_State時也會創建一個主線程,當然根據需要也可以調用lua_newthread來創建新的線程,但是整個虛擬機,只有一個全局的狀態global_State。
    全局狀態結構體中其他成員基本都是與內存管理和GC相關的。
    下面來看線程對應的數據結構lua_State的實現,代碼如下(lstate.h):
  1. 151 /* 
  2. 152 ** `per thread' state 
  3. 153 */  
  4. 154 struct lua_State {  
  5. 155   CommonHeader;  
  6. 156   lu_byte status;  
  7. 157   StkId top;  /* first free slot in the stack */  
  8. 158   global_State *l_G;  
  9. 159   CallInfo *ci;  /* call info for current function */  
  10. 160   const Instruction *oldpc;  /* last pc traced */  
  11. 161   StkId stack_last;  /* last free slot in the stack */  
  12. 162   StkId stack;  /* stack base */   
  13. 163   int stacksize;  
  14. 164   unsigned short nny;  /* number of non-yieldable calls in stack */  
  15. 165   unsigned short nCcalls;  /* number of nested C calls */  
  16. 166   lu_byte hookmask;  
  17. 167   lu_byte allowhook;  
  18. 168   int basehookcount;  
  19. 169   int hookcount;  
  20. 170   lua_Hook hook;  
  21. 171   GCObject *openupval;  /* list of open upvalues in this stack */  
  22. 172   GCObject *gclist;  
  23. 173   struct lua_longjmp *errorJmp;  /* current error recover point */  
  24. 174   ptrdiff_t errfunc;  /* current error handling function (stack index) */  
  25. 175   CallInfo base_ci;  /* CallInfo for first level (C calling Lua) */  
  26. 176 };  

    可以看到,lua_State結構跟其他可回收的數據類型類型,結構體帶用CommonHeader的頭,它也GC回收的對象之一。它主要包括以下成員信息:
lu_byte status:線程腳本的狀態,線程可能狀態如下(lua.h):

  1. 44 /* thread status */  
  2. 45 #define LUA_OK      0  
  3. 46 #define LUA_YIELD   1  
  4. 47 #define LUA_ERRRUN  2  
  5. 48 #define LUA_ERRSYNTAX   3  
  6. 49 #define LUA_ERRMEM  4  
  7. 50 #define LUA_ERRGCMM 5  
  8. 51 #define LUA_ERRERR  6  
  global_State *l_G:指向全局狀態;
    其他成員主要是數據棧和函數調用棧相關的,這也是lua_State結構中主要信息。還有成員ptrdiff_t errfunc是錯誤處理函數相關,也就是每個調用棧都有獨立的錯誤處理函數,以及調試相關的lua_Hook hook成員等。


3、總結
    在調用lua_newstate 初始化Lua虛擬機時,會創建一個全局狀態和一個線程(或稱爲調用棧),這個全局狀態在整個虛擬機中是唯一的,供其他線程共享。一個Lua虛擬機中可以包括多個線程,這些線程共享一個全局狀態,線程之間也可以調用lua_xmove函數來交換數據。


參考資料

http://www.cnblogs.com/ringofthec/archive/2010/11/09/lua_State.html lua API 小記3(lua虛擬機初始化)
http://blog.aliyun.com/795 Lua數據結構 — lua_State(六)
Lua 5.2.1源碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章