Lua中的线程和状态

1、概述

线程(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' state
153 */
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  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源码

时间: 2024-08-19 00:02:29

Lua中的线程和状态的相关文章

lua中实现异步资源读写

同样还是更新方面的需求,当我们检测到版本是新安装的以后,要进行upd目录清除.如果使用os.execute执行 rm -rf ooxx 是非常快的但由于os.execute一旦报错,那整个lua进程就杯具了.于是,我们最后换成了lfs来进行目录遍历并逐个删除.这样一来,文件数目如果过多,会等很久.于是想到使用异步的方式来处理. 一开始我尝试了lua的协程,我以为他和golang等语言中的一样.可以实现轻量级线程.于是我做了一个简单的测试 h = coroutine.creat( function

并发编程专题(三)-线程的状态

1.线程状态 Java中,线程的状态使用一个枚举类型来描述的.这个枚举一共有6个值: NEW(新建).RUNNABLE(运行).BLOCKED(锁池).TIMED_WAITING(定时等待).WAITING(等待).TERMINATED(终止.结束).但是大多数人的理解和上面的这六种还是有些差别,通常会加上阻塞状态,可运行状态,挂起状态.在API中 java.lang.Thread.State 这个枚举中给出了六种线程状态,这是Thread类描述线程状态的枚举类的源代码: public enum

Java中的线程的生命周期大体可分为5种状态

Java中的线程的生命周期大体可分为5种状态. ①NEW:这种情况指的是,通过New关键字创建了Thread类(或其子类)的对象 ②RUNNABLE:这种情况指的是Thread类的对象调用了start()方法,这时的线程就等待时间片轮转到自己这,以便获得CPU:第二种情况是线程在处于RUNNABLE状态时并没有运行完自己的run方法,时间片用完之后回到RUNNABLE状态:还有种情况就是处于BLOCKED状态的线程结束了当前的BLOCKED状态之后重新回到RUNNABLE状态. ③RUNNING

Java线程池中线程的状态简介

首先明确一下线程在JVM中的各个状态(JavaCore文件中) 1.死锁,Deadlock(重点关注) 2.执行中,Runnable(重点关注) 3.等待资源,Waiting on condition(重点关注) 4.等待监控器检查资源,Waiting on monitor(eg:如果使用System.out.println等需要分配计算机资源的时候线程会如此等待,主要还需看堆栈) 5.暂停,Suspended 6.对象等待中,Object.wait() 7.阻塞,Blocked(重点关注) 8

android 自定义adapter和线程结合 + ListView中按钮滑动后状态丢失解决办法

adapter+线程 1.很多时候自定义adapter的数据都是来源于服务器的,所以在获取服务器的时候就需要异步获取,这里就需要开线程了(线程池)去获取服务器的数据了.但这样有的时候adapter的中没有数据. 如下面的代码: 这就是在initData中异步获取服务器的数据,然后实例化adatper,再将adapter赋给listView. 2.initData()中的代码是: 这里线程要睡眠5秒钟,是为了模仿网络的耗时操作 3.Handler: 在Handler中接收到数据后给list赋值后,

学习java线程状态和看懂thread dump文件中的线程信息

线程的状态是一个很重要的东西,因此thread dump中会显示这些状态,通过对这些状态的分析,能够得出线程的运行状况,进而发现可能存在的问题.线程的状态在Thread.State这个枚举类型中定义: public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runn

线程的生命周期 - 理解Java中线程的状态

刚刚开始学cocos2-x,仅仅是按照教程把已经安了一般Android的开发环境的eclipse重新升级到安装好cdt和ndk就花了我几十小时,差点都要放弃了. 参考博客 D:\cocos2d-x\cocos2d-x-2.2.3\cocos2dx\platform\third_party\android\prebuilt 说说大概的过程: 下载ndk插件,ndk包,cdt插件.最开始我按照书上的下载了cocos2d-x 2.0.1,希望跟书上统一起来,这样以后学习的时候可以参考书上的也不会遇到太

jstack Dump日志文件中的线程状态

jstack Dump 日志文件中的线程状态 dump 文件里,值得关注的线程状态有: 死锁,Deadlock(重点关注)  执行中,Runnable 等待资源,Waiting on condition(重点关注) 等待获取监视器,Waiting on monitor entry(重点关注) 暂停,Suspended 对象等待中,Object.wait() 或 TIMED_WAITING 阻塞,Blocked(重点关注)   停止,Parked 下面我们先从第一个例子开始分析,然后再列出不同线程

Java中的线程状态转换和线程控制常用方法

Java 中的线程状态转换: [注]:不是 start 之后就立刻开始执行, 只是就绪了(CPU 可能正在运行其他的线程). [注]:只有被 CPU 调度之后,线程才开始执行, 当 CPU 分配给你的时间片到了, 又回到就绪状态, 继续排队等候. 线程控制的基本方法: isAlive(): 判断线程是否还活着, start 之后,终止之前都是活的; getPriority(): 获得线程的优先级数值; setPriority(): 设置线程的优先级数值(线程室友优先级别的);   Thread.