Lua中的线程和状态
来源:互联网 发布:java生成soap报文 编辑:程序博客网 时间:2024/06/05 00:33
1、概述
线程(thread)作为Lua中一种基本的数据类型,它代表独立的执行线程(independent threads of execution),线程类型是实现协程(coroutines)的基础,注意这里的线程类型不要与操作系统线程混淆,Lua的线程类型是Lua虚拟机实现一种数据类型。
从Lua脚本来看,一个协程就是一个线程类型,比如:
从实现角度来看,一个线程类型数据就是一个Lua与C交互的栈,每个栈包含函数调用链和数据栈,还有独立的调试钩子和错误信息。关于Lua与C交互的栈的可以参考之前写的一篇文章。线程类型数据与table数据类型类似,它也是需要GC来管理的。
当调用Lua C API中的大多数函数时,这些函数都是在特定的栈(或线程)上,因此在很多API中,第一个参数是lua_State*(线程类型对应的数据结构就是lua_State,或者说交互的栈就是用结构体lua_State来实现的),这个参数不仅表示了一个Lua状态,还表示一个记录在该状态中的线程。注意这里的状态,对一个虚拟机来说只有一个,而一个虚拟机可以包括多个线程(或者说交互栈)。这个状态也就是虚拟机的全局状态,我们可以通过调用函数luaL_newstate()来创建一个虚拟机的状态,该函数声明如下:
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):
其他成员主要是数据栈和函数调用栈相关的,这也是lua_State结构中主要信息。还有成员ptrdiff_t errfunc是错误处理函数相关,也就是每个调用栈都有独立的错误处理函数,以及调试相关的lua_Hook hook成员等。
线程(thread)作为Lua中一种基本的数据类型,它代表独立的执行线程(independent threads of execution),线程类型是实现协程(coroutines)的基础,注意这里的线程类型不要与操作系统线程混淆,Lua的线程类型是Lua虚拟机实现一种数据类型。
从Lua脚本来看,一个协程就是一个线程类型,比如:
local co = coroutine.create(function() print("hi") end)print(co) --output: thread: 0038BEE0准确来说,协程是一个线程外加一套良好的操作接口。
从实现角度来看,一个线程类型数据就是一个Lua与C交互的栈,每个栈包含函数调用链和数据栈,还有独立的调试钩子和错误信息。关于Lua与C交互的栈的可以参考之前写的一篇文章。线程类型数据与table数据类型类似,它也是需要GC来管理的。
当调用Lua C API中的大多数函数时,这些函数都是在特定的栈(或线程)上,因此在很多API中,第一个参数是lua_State*(线程类型对应的数据结构就是lua_State,或者说交互的栈就是用结构体lua_State来实现的),这个参数不仅表示了一个Lua状态,还表示一个记录在该状态中的线程。注意这里的状态,对一个虚拟机来说只有一个,而一个虚拟机可以包括多个线程(或者说交互栈)。这个状态也就是虚拟机的全局状态,我们可以通过调用函数luaL_newstate()来创建一个虚拟机的状态,该函数声明如下:
lua_State *lua_newstate (lua_Alloc f, void *ud);没错,在应用程序(比如用C++编写的)中,为了加载和执行Lua脚本,首先要调用的函数就是这个函数来初始化虚拟机。该函数在初始化虚拟机状态的同时,还是创建整个虚拟机的第一个线程(称为主线程),或者说是第一个交互栈。为了在已经初始化的全局状态中创建一个新的线程(或交互栈)可以调用函数lua_newthread,该函数声明如下:
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):
109 /* 110 ** `global state', shared by all threads of this state 111 */ 112 typedef struct global_State { 113 lua_Alloc frealloc; /* function to reallocate memory */ 114 void *ud; /* auxiliary data to `frealloc' */ 115 lu_mem totalbytes; /* number of bytes currently allocated - GCdebt */ 116 l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ 117 lu_mem GCmemtrav; /* memory traversed by the GC */ 118 lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ 119 stringtable strt; /* hash table for strings */ 120 TValue l_registry; 121 unsigned int seed; /* randomized seed for hashes */ 122 lu_byte currentwhite; 123 lu_byte gcstate; /* state of garbage collector */ 124 lu_byte gckind; /* kind of GC running */ 125 lu_byte gcrunning; /* true if GC is running */ 126 int sweepstrgc; /* position of sweep in `strt' */ 127 GCObject *allgc; /* list of all collectable objects */ 128 GCObject *finobj; /* list of collectable objects with finalizers */ 129 GCObject **sweepgc; /* current position of sweep in list 'allgc' */ 130 GCObject **sweepfin; /* current position of sweep in list 'finobj' */ 131 GCObject *gray; /* list of gray objects */ 132 GCObject *grayagain; /* list of objects to be traversed atomically */ 133 GCObject *weak; /* list of tables with weak values */ 134 GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ 135 GCObject *allweak; /* list of all-weak tables */ 136 GCObject *tobefnz; /* list of userdata to be GC */ 137 UpVal uvhead; /* head of double-linked list of all open upvalues */ 138 Mbuffer buff; /* temporary buffer for string concatenation */ 139 int gcpause; /* size of pause between successive GCs */140 int gcmajorinc; /* how much to wait for a major GC (only in gen. mode) */141 int gcstepmul; /* GC `granularity' */142 lua_CFunction panic; /* to be called in unprotected errors */143 struct lua_State *mainthread;144 const lua_Number *version; /* pointer to version number */145 TString *memerrmsg; /* memory-error message */146 TString *tmname[TM_N]; /* array with tag-method names */147 struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */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):
151 /*152 ** `per thread' state153 */154 struct lua_State {155 CommonHeader;156 lu_byte status;157 StkId top; /* first free slot in the stack */158 global_State *l_G;159 CallInfo *ci; /* call info for current function */160 const Instruction *oldpc; /* last pc traced */161 StkId stack_last; /* last free slot in the stack */162 StkId stack; /* stack base */ 163 int stacksize;164 unsigned short nny; /* number of non-yieldable calls in stack */165 unsigned short nCcalls; /* number of nested C calls */166 lu_byte hookmask;167 lu_byte allowhook;168 int basehookcount;169 int hookcount;170 lua_Hook hook;171 GCObject *openupval; /* list of open upvalues in this stack */172 GCObject *gclist;173 struct lua_longjmp *errorJmp; /* current error recover point */174 ptrdiff_t errfunc; /* current error handling function (stack index) */175 CallInfo base_ci; /* CallInfo for first level (C calling Lua) */176 };
可以看到,lua_State结构跟其他可回收的数据类型类型,结构体带用CommonHeader的头,它也GC回收的对象之一。它主要包括以下成员信息:
lu_byte status:线程脚本的状态,线程可能状态如下(lua.h):
44 /* thread status */ 45 #define LUA_OK 0 46 #define LUA_YIELD 1 47 #define LUA_ERRRUN 2 48 #define LUA_ERRSYNTAX 3 49 #define LUA_ERRMEM 4 50 #define LUA_ERRGCMM 5 51 #define LUA_ERRERR 6global_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源码
0 1
- Lua中的线程和状态
- Lua的线程和状态
- Lua的线程和状态
- Lua知识点3_线程和状态
- Lua学习总结——线程和状态
- Lua的线程和状态 及协程
- Lua的线程和状态 及协程
- lua中的线程详解
- 线程中的状态转换和Thread 中的sleep()方法
- .NET中的线程状态
- Java中的线程状态
- 线程状态和调度
- 线程状态和安全
- JAVA中的线程状态切换
- 学习java线程状态和看懂thread dump文件中的线程信息
- lua--lua中的类型和值
- java中的线程(一)——线程的五种状态和线程的创建方式
- 进程和线程的状态
- Project Euler:Problem 12 Highly divisible triangular number
- ubantu下如何完全彻底卸载mysql
- 英语六级、考研高频词汇
- OC视频笔记3.1(Foundation框架,NSObject里面的方法)3.2(isKindOfClass和isMemberOfClass)
- jackson的基本使用
- Lua中的线程和状态
- 队列(一)
- 象棋将帅问题
- 技术研究(发明)的途径及其手段概述-1
- 如何在Windows下给Vim安装YouCompleteMe
- xmu 1075: 安全网络 ver.2(最短路模型)
- DFS&Iteration Binary Tree Inorder Traversal
- 一篇文章读懂开源web引擎Crosswalk
- 解决Xcode断点调试值都为nil的问题