Original: http://blog.csdn.net/soloist/article/details/329381
Concurrency is the essential feature of the real world, and the technical means that smart computer scientists use to simulate concurrency is the multi-task mechanism. Generally, there are two types of multi-task technology: preemptive multitasking, which allows the operating system to decide when to execute the task. The other is cooperative multitasking, which gives the decision to the task and allows them to voluntarily give up execution when they think it is appropriate. The two multitasking methods have their own advantages and disadvantages. The inherent synchronization problem of the former makes the program often have unpredictable behaviors, while the latter requires the task to be highly self-disciplined.
Coroutine technology is a program control mechanism that was proposed as early as the 1960s S and can be used to conveniently implement collaborative multitasking. We rarely see coroutine in mainstream programming languages (such as C ++, Java, and Pascal), but now many dynamic scripting languages (Python and Perl) but they all provide coroutine or similar mechanisms. The most prominent one is Lua.
The coroutine implemented by Lua is an asymmetric coroutine, or a semi-symmetric coroutine, or a semi-coroutine ). This coroutine mechanism is called asymmetric because it provides two types of operations that pass program control: one is to call coroutine (through coroutine. resume); the other is to suspend the coroutine and return control of the program to the cooutine caller (via coroutine. yield ). An asymmetric coroutine can be seen as its caller. The relationship between the two is very similar to that between the routine and Its caller. Since there is a non-symmetric coroutine, of course there is also a symmetric coroutine, which is characterized by only one operation that passes control of the program, that is, the control is directly transferred to the specified coroutine. There was a saying that the ability of symmetric and asymmetric coroutine mechanisms is not equivalent, but in fact it is easy to implement the latter based on the former. Next we will use code to prove this fact.
-- Coro. Lua, symmetric coroutine Library
-- The code is taken from the paper "coroutines in Lua"
--Www.inf.puc-rio.br /~ Robert to/docs/corosblploud
Coro = {}
-- Coro. Main is used to identify the main function of the program.
Coro. Main = function () End
-- The Coro. Current variable is used to identify the coroutine with control,
-- That is, the running current coroutine
Coro. Current = Coro. Main
-- Create a new coroutine
Function Coro. Create (f)
Return coroutine. Wrap (function (VAL)
Return nil, F (VAL)
End)
End
-- Pass control and specified data Val to coroutine K
Function Coro. transfer (K, Val)
If Coro. Current ~ = Coro. Main then
Return coroutine. Yield (K, Val)
Else
-- Control distribution cycle
While K do
Coro. Current = K
If K = Coro. Main then
Return Val
End
K, val = K (VAL)
End
Error ("coroutine ended without transfering control ...")
End
End
If you still don't understand the above program for the time being, it doesn't matter. Let's take a look at how to use this library and analyze it later. The following is an example:
Require ("Coro. Lua ")
Function foo1 (N)
Print ("1: foo1 inclued value" .. n)
N = Coro. Transfer (foo2, N + 10)
Print ("2: foo1 converted ed value" .. n)
N = Coro. Transfer (Coro. Main, N + 10)
Print ("3: foo1 converted ed value" .. n)
Coro. Transfer (Coro. Main, N + 10)
End
Function foo2 (N)
Print ("1: foo2 converted ed value" .. n)
N = Coro. Transfer (Coro. Main, N + 10)
Print ("2: foo2 converted ed value" .. n)
Coro. Transfer (foo1, N + 10)
End
Function main ()
Foo1 = Coro. Create (foo1)
Foo2 = Coro. Create (foo2)
Local n = Coro. Transfer (foo1, 0)
Print ("1: Main received value" .. n)
N = Coro. Transfer (foo2, N + 10)
Print ("2: Main received value" .. n)
N = Coro. Transfer (foo1, N + 10)
Print ("3: Main received value" .. n)
End
-- Set main as the main function (coroutine)
Coro. Main = Main
-- Set Coro. Main as the current coroutine
Coro. Current = Coro. Main
-- Start to execute the main function (coroutine)
Coro. Main ()
The preceding example defines a main function. The whole program begins with it and ends with it. Why do we need such a main function? As mentioned above, the control of a program can be freely transmitted between symmetric coroutines. It doesn't matter who belongs to each other and is at the same level, however, the application must have a start point. Therefore, we define a main function to ignite the fuse of the program running. Although the coroutine process is equal, the main function as the motive power of the program running still enjoys a special status (which is absolutely equal in the world !), Therefore, our library uses a Coro. main variable to save the main function, and set it to the current coroutine before it is executed (although the above main is actually just a common function rather than a real coroutine, but this does not have much to do with it. In the future, the main function will also be called the main coroutine ). The result of the example is:
1: foo1 inclued value 0
1: foo2 inclued value 10
1: Main received value 20
2: foo2 inclued value 30
2: foo1 inclued value 40
2: Main received value 50
3: foo1 inclued value 60
3: Main received value 70
The execution sequence of coroutine is: Main-> foo1-> foo2-> main-> foo2-> foo1-> main-> foo1-> main.
In the Coro. transfer (K, Val) function, k is the coroutine that will receive control of the program, while Val is the data transmitted to K. If the current coroutine is not the main coroutine, tansfer (K, Val) simply uses coroutine. yield (K, Val) suspends the current coroutine and returns two pieces of data, that is, the next stop of control of the program and the data transmitted to it; otherwise, it enters a dispatch loop, this loop (re) starts (resume) K coroutine, waits for it to be executed to suspend (suspend), and determines the coroutine to be restarted based on the data returned by the coroutine at this time. From the application example, the coroutine and coroutine seem to use transfer to directly transmit control, but in fact the transfer still passes the master coroutine. Each one is called in the main process (compared to Coro. current and Coro. if the main is the same, it can be determined) the transfer is equivalent to a coroutine manager, which continuously (re) starts a coroutine and gives control, then, when the coroutine is suspended, the control is reclaimed, and then the next coroutine starts ..., this action will not stop unless <1> the coroutine started (re-) is the master coroutine; <2> the next destination where a coroutine does not provide control. Obviously, the master coroutine takes control of each round of scheduling cycle at the beginning, in the loop process, if the next stop of control is the main coroutine, it means that the master coroutine transfer operation that handed in the control should end, so the function returns Val directly to end the loop. For the case <2>, because Coro. the body function of the coroutine created by create (f) is actually function (VAL) return nil, F (VAL) end, so when the last instruction of function f is not transfer, the coroutine will be executed and the return values of nil and function F will be returned together. If K is such a coroutine, after transfer executes the K, val = K (VAL) Statement, the K value becomes nil, which is considered an error, because the program cannot determine who the next coroutine should be started at this time. Therefore, in a symmetric model, each coroutine (of course, the main coroutine) must explicitly pass control to other coroutines. Based on the above analysis, the application instance control is assigned as follows:
First round of assignment: Main-> foo1-> main-> foo2-> main (end)
Second round of assignment: Main-> foo2-> main-> foo1-> main (end)
Round 3: Main-> foo1-> main (end)
The symmetric coroutine mechanism has great freedom because it can directly specify the target for control transfer, but the cost of such freedom is to sacrifice the program structure. If the program is a little more complex, it is difficult for a very experienced programmer to have a comprehensive and clear grasp of the program process. This is very similar to the GOTO statement, which allows the program to jump to any place you want to go, but it is hard for people to understand the program full of Goto. Asymmetric coroutines have a good hierarchical relationship. (re) starting these coroutines is very similar to calling a function: The coroutines started by (re) Get control and start execution, then it suspends (or ends) and returns the control to the coroutine caller, which is exactly the same as the structured programming style advocated by computer pioneers.
To sum up, the asymmetric coroutine provided by Lua not only has the same powerful capabilities as the symmetric coroutine, but also avoids programmers from abusing the mechanism to write program with chaotic structures.