[In-depth Lua] Understand the most powerful feature in Lua - coroutine (coroutine) [transfer]

 

###coroutine basics

The full name of the coroutine supported by Lua is called collaborative multithreading. Lua provides a separate run line for each coroutine. However, unlike multithreading, a coroutine is only suspended after explicitly calling the yield function, and only one coroutine is running at a time.

Lua puts all its coroutine functions into the coroutine table. The main functions are as follows

sheet

Extract a piece of Yunfeng 's code to explain the working mechanism of the coroutine in detail. In this code, the interaction between the main thread and the coroutine co is shown:

<!-- lang: lua -->
function foo(a)
    print("foo", a)
    return coroutine.yield(2 * a)
end

co = coroutine.create(function ( a, b )
    print("co-body", a, b)
    local r = foo(a + 1)
    print("co-body", r)
    local r, s = coroutine.yield(a + b, a - b)
    print("co-body", r, s)
    return b, "end"
end)

print("main", coroutine.resume(co, 1, 10))
print("main", coroutine.resume(co, "r"))
print("main", coroutine.resume(co, "x", "y"))
print("main", coroutine.resume(co, "x","y"))

 

The following is the running result

co-body 1 10
foo 2
main true 4
co-body r
main true 11, -9
co-body x y
main false 10 end
main false cannot resume dead coroutine

 

###coroutine and subroutine (subroutine) comparison

The start of a subroutine is the only entry point, and once return completes the execution of the subroutine, an instance of the subroutine will only run once.

But the coroutine is different. The coroutine can use yield to switch to other coroutines, and then reenter ** (reenter) through the resume method to the place where yield was last called, and pass the parameters of resume as the return value to the desired Reentrant coroutines. But none of the coroutine's state has been changed, 就像一个可以多次返回的subroutine,

The subtlety of coroutines lies in 协作this concept. Let's use the producer and consumer problems to demonstrate the basic application of coroutines. Note: The following pseudocode is written with Lua in mind

var q = queue ()

 

Producer's Pseudocode

loop
    while q is not full
        create product
        add the items to q
    resume to consumer

 

Consumer Pseudocode

loop
    while q is not empty
        consume product
        remove the items from q
    yield

 

###coroutine and callback comparison

Coroutine is often used to compare with callback, because in general, coroutine and callback can achieve the same function, namely asynchronous communication, such as the following example:

<!-- lang: lua -->
bob.walkto(jane)
bob.say("hello")
jane.say("hello")

 

It seems to be right, but in fact, because these actions are walkto and say 需要一定时间才能做完的, if this program is written in this way, it will cause bob to say hello to jane while walking, and then jane also says to bob at the same time hello, the whole process is very confusing.

If implemented using callbacks, the code example is as follows:

<!-- lang: lua -->
bob.walto(function (  )
    bob.say(function (  )
        jane.say("hello")
    end,"hello")
end, jane)

 

That is, the walto function calls back the say function, and the say function calls back the next say function, so the callback looks very confusing, making it impossible to see the meaning of this code at a glance.

If you use coroutine, you can use the following notation:

<!-- lang: lua -->
co = coroutine.create(function (  )
    local current = coroutine.running
    bob.walto(function (  )
        coroutine.resume(current)
    end, jane)
    coroutine.yield()
    bob.say(function (  )
        coroutine.resume(current)
    end, "hello")
    coroutine.yield()
    jane.say("hello"end)

coroutine.resume(co)

 

In the above code, once an asynchronous function is called, the coroutine will use the coroutine.yield() method to temporarily suspend the coroutine, and add coroutine.resume(current) to the corresponding callback function to make it return to the current in the executing coroutine.

However, there are many repetitions in the above code, so the repeated code can be encapsulated by encapsulation

<!-- lang: lua -->
function runAsyncFunc( func, ... )
    local current = coroutine.running
    func(function (  )
        coroutine.resume(current)
    end, ...)
    coroutine.yield()
end

coroutine.create(function (  )
    runAsyncFunc(bob.walkto, jane)
    runAsyncFunc(bob.say, "hello")
    jane.say("hello")
end)

coroutine.resume(co)

 

In this way, it is not necessary to change all the previous callback functions, and the problem of asynchronous calls can be solved by means of Ctrip, which makes the structure of the code very clear.

 

 

 

 

Reprinted from: https://my.oschina.net/wangxuanyihaha/blog/186401

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324878078&siteId=291194637