Recording

Commit volatile memory to persistent append-only log

0%

Lua coroutine 和 C# async

Lua coroutine

Lua coroutine 是协作式多任务的一种实现。coroutine 经常被 C/C++ 这类没用原生轻量级多任务实现的宿主语言通过异步方式实现 Lua 层的同步调用。

这种同步调用的流程:

val, err = synchronous_call_in_lua(...)    -- function call in lua coroutine
    ==> coroutine.yield
        ==> C/C++ 发起异步请求会话
        ==| (time consumed)
        ==| C/C++ 异步会话完成(消息/回调)
    <== coroutine.resume
val, err = ... -- <== return from function
do_someting(...)

这个流程很像阻塞式的系统调用:

n = read(fd, buf, len)        // system call in userland
    ==> syscallenter(...)     // in kernel mode
        |=> sys_read(...)     // in system call
        |== kernel activity   // asynchronous wait if data is not available yet
        |==
    <== syscallret(...)       // return from syscall
n = ...                       // return from read in userland

C# async/await

async/await 是 C# 5 引入的,用来简化异步编程的设施,其本质是由编译器实施的 CPS 变换。

C# 5.0 introduces the async and await keywords. These keywords let you write asynchronous code that has the same structure and simplicity as synchronous code, as well as eliminating the “plumbing” of asynchronous programming. The await keyword simplifies the attaching of continuations.

即将如下代码:

var result = await expression;
statement(s);

转换为:

var awaiter = expression.GetAwaiter();
awaiter.OnCompleted (() =>
{
    var result = awaiter.GetResult();
    statement(s);
});

从上面的代码可以看出,这段代码是非阻塞的, statement(s) 是在 callback 中执行的。

总结

Lua coroutine 和 C# async/await 都可以用来编写形如同步的代码。Lua coroutine 中的函数调用是同步阻塞的(从调用者角度),由 C/C++ 通过异步方式实现,其执行流程和一般的同步阻塞的系统调用类似。C# async/await 则是由编译器实施的 CPS 变换,是异步非阻塞的。

下面一段 C# 代码的流程,如果由 Lua 编写,输出顺序则相反。

//Title of this code
//Rextester.Program.Main is the entry point for your code. Don't change it.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Rextester {

    public class Program
    {

        static readonly CountdownEvent _countdown = new CountdownEvent(1);
        static int _value = 5;

        public static void Main(string[] args)
        {
            delay(10);
            dump("return from delay()");
            _value = 10;
            _countdown.Wait();
        }

        async static void delay(int msecs) {
            await Task.Delay(msecs);
            dump("in delay():");
            _countdown.Signal();
        }

        static void dump(string prefix) {
            Console.WriteLine("{0}: value = {1}", prefix, _value);
        }

    }

}

输出: return from delay(): value = 5 in delay():: value = 10

阅读