Date: 2014.8.14part IV the C API
31 threads and States Lua actually does not support true multithreading, that is, the preemptive threads shared memory. There are two reasons: First, ansi c does not support this mechanism, so there is no proper method in Lua to implement this mechanism; second, the most important thing is that the author thinks that it is not a good idea to support true multithreading in Lua. Multithreading is a mechanism developed for low-level development languages. The signal, monitoring, and other synchronization mechanisms in the concept of multithreading depend on the operating system rather than applications. Therefore, it is difficult to detect and correct bugs caused by multithreading, some of these bugs may cause security problems. In addition, multithreading may also affect the performance of the program running process due to information synchronization, for example, affecting the program memory allocation. The problems caused by multithreading are mainly caused by the combination of preemptive thread and Memory sharing. Therefore, Lua avoids these problems by using non-preemptive thread and non-shared memory. The Lua thread (also called a collaborative program) is collaborative, so it avoids the problem caused by unpredictable thread switching, and The Lua State does not share the memory, therefore, it provides a good foundation for concurrency. This chapter focuses on these two points.
31.1 multiple threadsThreads in Lua are essentially a collaborative program. We can think that a coprocessor is a thread with a good interface, or we can think that a thread is a coprocessor with a low-level API. From the perspective of c api, it is very useful to think that a thread is a stack. From the implementation process perspective, the thread is indeed a stack. Each stack stores information about all unexecuted calls, parameters of each call, and local variables. In other words, a stack has all the information that a thread needs to run again. Therefore, multithreading means multiple independent stacks. In fact, all the LUA-c api functions we discussed previously are operated on a specific stack. This is known from the very beginning. But how do we know which stack we want to operate? Will the operation be wrong? The key point here is the first parameter type lua_state when we call a function. This parameter represents not only a Lua state, but also a thread included in this state. Whenever a Lua state is created, Lua will automatically create a new thread in this state, and then return a lua_state representing this thread. This main thread will never be recycled (this ?? Whether to remove the threads created by calling lua_newthread, that is, the thread automatically created by Lua State ). When you use lua_close to close the state, the thread will be released along with the State. You can call lua_newthread in a state to create another thread:
lua_State *lua_newthread(lua_State *L);
The above function returns a lua_state pointer representing the new thread and pushes the new thread into the stack as the thread type, for example:
L1 = lua_newthread(L);
After the above steps, we have two threads, and both threads are actually in the same Lua state. Each thread has its own independent stack, and the new thread L1 has an empty stack, while the l thread stores the new thread at the top of its stack. For example:
E. g. printf ("% d \ n", lua_gettop (L1); -- 0 the stack of the new thread is an empty stack printf ("% s \ n", lual_typename (L, -1); -- thread indicates a thread at the top of the stack, that is, the new thread.Except for the main thread, the other threads are the objects managed by the garbage collection, which is the same as the other objects in Lua. When a new thread is created and pushed to the stack as the thread type, this thread will not be recycled. We should ensure that we do not use threads that are not sure whether they are in the stack. Every call to Lua's API may trigger the collection of threads not in the stack, even if the thread is still in use, such:
lua_State *L1 = lua_newthread(L);lua_pop(L,1); /* L1 is garbage*/lua_pushstring(L1,"helloe"); /* danger */
When lua_pushstring is called, Garbage Collector may be triggered to recycle L1, even if this thread is still in use. To avoid unexpected errors, always reference the threads in use. For instance in the stack of an anchored thread or in the registry. the newly created thread can be used the same as the main thread. Elements can be pushed and introduced from the stack of the thread, and functions can be called using the stack. Here we introduce a function lua_xmove (L1, L, 1), which is used to move the stack data in the same state, such as lua_xmove (F, t, n ), it is to introduce an element from Stack F and then push it to stack T. In this operation, we do not even need to create additional threads, but only need to process them in the main thread. The premise of using multithreading here is to implement a collaborative program so that we can delay or resume the execution of a thread. To resume execution of a function, you need to use the function lua_resume to support: int lua_resume (lua_state * l, lua_state * From, int nary). This is the case if we use lua_pcall, if we want to run a collaborative program, we can use the lua_resume function: the function to be called, the parameter is pushed to the stack, and the function parameter NARG represents the number of parameters passed. (The from parameter refers to the state of the thread that executes the call ). The main behavior is similar to lua_pcall, but there are three differences: 1. lua_resume does not represent the number of returned values, and the function always returns all returned results; 2. If no parameter is provided for the message handle, the error will not be triggered in the stack. Therefore, you can check the stack after the error (~~~ 3. If the program is suspended, lua_resume returns a special code: lua_yield, and leaves the thread in the State for future calls. The coprocessor can also call C functions, which can also call back other Lua functions.
31.2 Lua stateEach time you call the lual_newstate function, a new Lua state will be created. different Lua states are independent of each other, which means that the events in a State do not affect other States, and that data interaction cannot be performed directly between States, instead, C code is required. For example, lua_pushstring (L2, lua_tostring (L1,-1); the above Code transfers the elements at the top of the L1 stack to L2 using the c api. Because we need to use c api to implement data interaction, the Data Type passed must be the type supported by C, data such as table needs to be converted in an appropriate way before it can be transmitted. In a system that supports multiple threads, it is worth exploring to create an independent Lua state structure for each thread. The result of this structure is similar to processes in Unix. Here we can get concurrency without sharing memory. The rest of this section is the introduction of using POSIX Threads to create independent Lua States for each thread mentioned above. It is a bit difficult to understand and no longer takes notes. I will try to understand it later.
Programming in Lua 3 Reading Notes (17th)