lua 如何支持多线程操作?

栈的理解

要向了解lua多线程,你就要知道栈的含义。这里我就直接摘录博客上的一些见解:


栈就是一段特殊内存,什么是栈呢?举个例子,一个只有上面打开的盒子,现在有三本书离散数学、c语言、汇编语言,需要将这三本书一本一本的放进去,先将离散数学放进去,然后c语言,接着汇编语言,现在又需要将三本书拿出去,只能先拿汇编语言,再拿c语言接着再拿离散数学,栈就是这样的特点,后进先出。

栈的大小怎么确认呢?这是靠我们自己决定的,如何确定这段内存为栈,就需要两个寄存器,段寄存器ss和存放偏移地址的寄存器sp,比如我们决定10000-1000f为寄存器那么ss:sp 一开始应该为 1000:0010执行栈有两个指令push,pop,push是入栈执行过程是先sp+2之后在把数据放进去,pop指令是先出栈,先将指令放进栈接着再sp-2,就好像把东西放进去房间一样,需要先开门再把东西放进去,把东西拿出去,需要把东西拿出去再关门,东西就相当于需要操作的数据,开门和关门就相当于sp+2和sp-2。

栈最大为64kb,这和sp的寻址能力有关,比如10000-1ffff为栈,那么ss:sp一开始应该指向那里呢?按照之前的算法sp应该为ffff+1 = 10000但是sp只能储存4个字节所以sp = 0,当栈满了之后sp还是为0,这个时候再次入栈0-2 = fffffffe,所以sp = fffe,之后再次入栈的话就会将原数据给覆盖掉,所以要尽量避免这种情况。


lua的运行

首先我们需要知道,lua是解释性语言。是在执行的时候才分配堆栈空间。通过查看lua的源码,我们可以知道,在main函数的开端,lua就创建了一个全局的L(状态机),这个状态机可以说是lua的核心所在。它保存了栈的地址。

当执行lua脚本时,lua会将全局的变量和function记录在堆中,当执行代码段是,就会将一些局部变量和参数压到栈中进行处理。这一切和c语言的解析是一样的。

多线程

lua是不支持多线程的,一般都是协同来调用的。但是lua却可以调用c函数。于是,我们通过lua调用C接口起一个线程,实现lua多线程的使用。子线程再调用lua中的function。就可以通过子线程获取一些数据。单纯的人儿,以为一切都是美好的。问题就出现C调用lua中的function。将数据传给lua。

C调用lua

我们知道C也是可以调用lua的function的,一般的操作是:
1. 在lua中调用C函数,将需要注册的function,作为参数传给C函数
2. C将获取到的function和L(状态机)进行保存。
3. C通过向L压栈,将function和一些参数压入。通过lua_call函数进行调用。
根据上述的解释,我们可以知道。其中C和lua通过通信的是L(状态机)。压入栈之后,通过lua_call,就会进入lua的状态中。lua会处理栈中的内容。

问题所在

核心问题就是C调用lua的L和lua的L是同一个L。这样就出现一个问题,当主线程的lua脚本才进行压栈操作,而子线程中也进行压栈操作,那岂不是乱了套?在一开始就不应该成功的,为什么会这样呢?通过查看代码,发现lua对进行堆操作的函数中,都加上了线程锁。当主线程进行栈操作时,子线程是不可以对栈进行操作的。也就是说,子线程理论上是不会运行的,会卡在栈操作的函数那里。

但是为什么我们在运行的时候并没有出现这个现象呢?通过代码的查询,发现是主线程中有sleep函数,并且子线程中有阻塞,所以能够在几个线程中切换。 如果主线程的while循环中没有sleep,那么就会很快的出现问题。因此,lua从底层就是不支持多线程的。

为什么使用协同

如果你搜索lua多线程,大多数都会写搜索到协同程序。

每一个协程有自己的堆栈,自己的局部变量,可以通过yield-resume实现在协程间的切换。不同之处是:Lua协程是非抢占式的多线程,必须手动在不同的协程间切换,且同一时刻只能有一个协程在运行。并且Lua中的协程无法在外部将其停止,而且有可能导致程序阻塞。

正如上诉,协同拥有自己的堆栈,那是用来避免和其他堆栈冲突的。但是两者之间想要通信,就不能通过栈了。因为栈的不同,压入的数据在另一端是无法接收到的。
但是,我们可以通过一个全局变量进行通信。比如,子线程通过协同的堆栈进行调用lua里面的function。在function中获取传入的值,将它赋值给一个全局变量。那么主线程也能够调用了。

你可能感兴趣的:(linux,C语言,上班日志)