《Programming in Lua 3》读书笔记(二十七)

日期:2014.8.14

PartⅣ The C API

31 Threads and States

Lua实际上是不支持真正的多线程的,即preemptive threads 共享内存的情况。有两个原因:第一个,是ANSI C不支持,所以Lua中就没有合适的方法来实现这个机制;第二个原因,最重要的是作者认为Lua中支持真正的多线程不是个好主意。

多线程是给低级开发语言而发展出的机制。多线程概念中的信号和监控等同步机制机制都是依赖与操作系统而不是依赖于应用程序的,因此很难去发现和修正由多线程造成的bug,而且这些bug中的一些可能会造成安全问题。不仅如此,多线程也可能会对程序运行过程由于需要做信息同步而造成性能影响,如影响程序的内存分配。

由多线程带来的问题主要是从preemptive thread 和 内存共享结合 中产生的,因此Lua中通过使用 non-preemptive thread 和不共享内存来避免这些问题。Lua的线程(也叫协同程序)是协作的,因此避免了由不可预期的线程切换所带来的问题,且Lua的State是不共享内存的,因此在为并发性提供了一个良好基础。这一章主要是讲这个两点。

31.1 Multiple Threads

Lua中的线程本质上是一个协同程序。我们可以认为一个协同程序就是一个线程加上一个良好的接口,或者我们可以认为一个线程就是带一个低等级API的协同程序。

从C API角度来看,认为一个线程就是一个栈是挺有用的。而从实现过程角度来看,线程确实是一个栈。每个栈保存所有未执行的调用、每次调用的参数和局部变量等信息,换句话来说一个栈拥有一个线程要准备再次运行的所有信息。因此,多线程就是意味着多个独立的栈。

实际上看,我们之前讨论的各个Lua-C 的API函数,都是在一个特定的栈上操作的,这个从一开始就知道了。但是我们又是如何知道我们要操作具体的哪个栈呢?会不会操作错了?这里关键点就在于我们每次调用函数时的第一个参数类型lua_State ,这个参数不仅代表一个lua state,也同时包括在这个state中的thread。

无论何时创建了一个Lua state,Lua都会自动在这个state中创建一个新的线程,然后返回一个代表这个线程的lua_State。这个主线程是从不会被回收的(这个??是否指的除去调用lua_newthread额外创建的这些线程,即创建lua state自动创建的这个线程)。当你用lua_close 关闭了这个state的时候,线程会随同state一起释放。

可以在一个state中通过调用lua_newthread 来创建另外的thread:

lua_State *lua_newthread(lua_State *L);

上面这个函数返回一个代表这个新线程的lua_State 型指针,并且同时将这个新的线程作为thread类型推进栈中,如:

L1 = lua_newthread(L);

经过上面这步,我们就有了两个线程了,且两个线程都实际上在同一个lua state内部。每个线程都有自己独立的栈,新的线程L1有个空的栈;而L线程则将新的线程存储在其自身栈的栈顶。如:

e.g.
printf("%d\n",lua_gettop(L1));                    --0 新的线程的栈是一个空的栈
printf("%s\n",luaL_typename(L,-1));           --thread 代表在栈顶是一个线程,即新的那个线程

除了主线程之外,其余的这些线程都是受garbage collection管理的对象,和Lua其余的这些对象一样。当创建了一个新的线程,以thread类型将这个线程推进至栈中,保证了这个线程不会被回收掉。我们应该确保不使用那些不确定还是否在栈中的线程。每次调用Lua的API都可能会触发回收那些不在栈中的线程,即便这个线程还在使用中,如:

lua_State *L1 = lua_newthread(L);
lua_pop(L,1);     /* L1 is garbage*/
lua_pushstring(L1,"helloe");     /* danger */

调用lua_pushstring的时候可能会触发garbage collector回收掉L1,即便此时仍然在使用这个线程。为了避免遇到不可预期的错误,应该始终对我们在使用中的线程保持引用,for instance in the stack of an anchored thread or in the registry.

新创建的线程,可以和使用主线程一样使用。可以从该线程的栈中推进和推出元素,可以使用栈来调用函数等等。这里介绍一个函数lua_xmove(L1,L,1),这个函数的作用是做同一个state中的栈的数据移动,如lua_xmove(F,T,n),就是从栈F中推出一个 元素,然后推进至栈T中。做这个操作我们甚至都不需要创建额外新的线程,只需要在主线程中处理就可以了。在这里使用多线程的前提是实现协同程序,以便我们可以延缓或者恢复某个线程的执行。恢复执行某个函数,在这里就需要函数:lua_resume
来支持:

int lua_resume(lua_State *L,lua_State *from ,int nary);

如我们使用lua_pcall 一样,如果我们想运行一个协同程序,我们可以使用lua_resume函数:将要调用的函数,参数推进至栈中,函数参数narg代表传递的参数的数量。(from 参数指的是执行这个调用的线程所在的state)。主要的行为类似于lua_pcall,但还是有三个不同的地方:1、lua_resume没有代表返回值数量的参数,函数总是会返回所有的返回结果;2、没有给消息句柄用的参数,错误不会在栈中触发,因此可以在错误之后检测栈(~~~没看懂);3、如果在运行的这个程序被挂起了,lua_resume
返回一个特殊代码:LUA_YIELD ,且将在state中的线程保留下来留给之后重新调用。

协同程序也能调用C函数,这些C函数也能回调其他的Lua函数。

31.2 Lua State

每次调用luaL_newstate函数将会创建一个新的lua state.不同的Lua state相互之间是独立的,这就意味着一个state内部的事件是不会对其他的state产生影响的,也意味着不能在state间直接进行数据交互,而是需要借助C代码。如:

lua_pushstring(L2,lua_tostring(L1,-1);

上述代码将L1栈顶的元素借助C API传递到了L2中。因为我们是需要借助C API来实现数据交互的,所以传递的数据类型必须是C所支持的类型,像table之类的数据需要借助合适的方式首先进行转换才可以传递。

在支持多线程的系统中,为每个线程创建独立的Lua state这个结构值得去探讨一番。这个结构的结果类似与UNIX中的processes,在这里我们可以不shared memory就可以得到并发性。

这个小节剩下的部分是作者介绍他用POSIX 线程实现上述提到的为每个线程创建独立的Lua state,感觉有点难懂,就不再笔记了。留给以后再深入理解一番。

时间: 2024-10-20 06:10:00

《Programming in Lua 3》读书笔记(二十七)的相关文章

《Distributed Programming With Ruby》读书笔记二 Security and ID Conversion (Part1.1-2)

Security Although DRb provide some security, they fall short of a full, comprehensive solution. This can make DRb less than desirable in a lot of real world situations. However, in situations where security is a lesser concern,such as prototyping and

《Programming in Lua 3》读书笔记(二十二)

日期:2014.8.6 PartⅣ The C API 26 Extending Your Application 使用Lua很重要的一点是用来做配置语言.配合主语言做一些功能的配置. 26.1 The Basics 有的时候程序需要配置一些功能信息,很多时候可能有许多别的方法比用lua做配置要更简单:如使用环境变量或者读取文件,读取文件涉及到文件的解析.如果使用Lua进行配置的话,相当于用lua文件替代了要读取的如csv.txt文件等. 使用Lua进行配置的时候,就需要使用Lua API去控制

《Programming in Lua 3》读书笔记(二十一)

日期:2014.8.1 PartⅣ The C API 25 An Overview of the C API Lua是一种嵌入式语言.这就意味着Lua不是单独存在的,而是可以通过一系列的标准库将lua的特性嵌入至其他应用模块中. Lua以Lua interpreter(lua的解释器?)来解决了其不是独立程序,我们直到现在却又能独立使用Lua的问题.这个解释器是一个小型的程序(不超过500行代码),使用lua的标准库来实现独立解释程序,这个程序将处理与用户的交互等操作交给lua的标准库,这些库

代码的未来读书笔记<二>

代码的未来读书笔记<二> 3.1语言的设计 对Ruby JavaScript Java Go 从服务端客户端以及静态动态这2个角度进行了对比. 这四种语言由于不同的设计方针,产生了不同的设计风格. Header 客户端 服务端 动态 Html5 Ruby 静态 Java Go 静态动态 静态:无需实际运行,仅根据程序代码就能确定结果. 动态:只有到了运行时才能确定结果.不过无论任何程序,或多或少都包含的动态的特性. 动态运行模式 运行中的程序能识别自身,并对自身进行操作.对程序自身进行操作的编

《卓有成效的程序员》----读书笔记二

六大方面对比Launchy和TypeAndRun(TAR) 对于快速启动工具,很多人都有自己的偏好,多次听到朋友介绍Launchy的好,虽然自己一直在使用着TAR,还是克制不住对于好软件的渴求,下载Launchy进行试用.很多软件都是有一个试用期的,也许新的软件确实不错,但是你习惯了以前使用的那个软件.今天就比较客观的将Launchy和TAR进行一下对比,从界面.上手速度到功能.自定义,以及软件的稳定性.占用资源进行详细的比较. [界面美观]Launchy:毫无疑问这是它的强项.1.0正式版自带

《R实战》读书笔记二

第一章 R简介 本章概要 1安装R 2理解R语言 3运行R程序 本章所介绍的内容概括如下. 一个典型的数据分析步骤如图1所示. 图1:典型数据分析步骤 简而言之,现今的数据分析要求我们从多种数据源中获取数据.数据合并.标注.清洗和分析,并且把分析的结果进行展示,形成报告或者系统,辅助决策.R能够满足现今数据分析的要求. 为什么用R? R是一个适合统计分析和绘图的环境与语言.它是开源.免费的,获得世界范围社区支持.统计分析和绘图工具已经很多了,例如:SPSS,SAS,Excel,Stata和Min

《学会提问》读书笔记二

<学会提问>读书笔记二 因为书中的小点知识和思考太多,我从这篇笔记开始就只记我害怕会遗忘的知识,思考过程就不提及了. 弱势批判性思维和强势批判性思维 弱势批判性思维的目的是用批判性思维来反驳.抵制那些和你意见不同的论述最终就是为了看到那些与你主张不一致的人服服帖帖的甘心认输,但是这样就意味着,你对于是否接近真理和发扬美德漠不关心,实际上也摧毁了批判性思维潜在的人性的一面和不断发展进步的特征.而且我认为,弱势批判性思维的出发点是自私的,他们盲目地认为自己的观点就是正确的,用批判性思维来批判其他人

《大型网站技术架构》读书笔记二:大型网站架构模式

一.分层 最常见的架构模式,将系统在横向维度上切分成几个部分,每个部分单一职责.网站一般分为三个层次:应用层.服务层和数据层,其具体结构如下图所示: 通过分层,一个庞大系统切分成不同部分,便于分工合作和维护. 但是,分层架构也有一些挑战:①必须合理规划层次边界和接口:②禁止跨层次的调用及逆向调用. 二.分割 分割是在纵向方面对软件进行切分->将不同的功能和服务分割开来,包装成高内聚低耦合的模块单元,有助于软件开发和维护,还便于不同模块的分布式部署,提高网站的并发处理能力和功能扩展能力. 三.分布

《How Tomcat Works》读书笔记(二)

<How Tomcat Works>读书笔记(二) 这是<How Tomcat Works>第一二章的读书笔记.第一张主要写了一个静态资源处理的web服务器,第二章加了对servlet的处理. 1. 概述 1.1 架构 HttpServer:表示Http服务器,与客户端通信,处理Http请求. StaticResourceProcessor:对静态资源请求进行处理. ServletProcessor:对Servlet资源请求进行处理. Request:表示Http请求,实现了Ser