• 对lua协程的一点理解


    读《Programming In Lua》协程那一章,比较困惑的还是procuer-consumer那个例子:

    function consumer(prod)
        while true do
            local x = receive(prod)
            print(x)
        end
    end
    
    function receive(prod)
        local status, value = coroutine.resume(prod)
        return value
    end
    
    function send(x)
        coroutine.yield(x) -- go back to where resumed
    end
    
    function producer()
        return coroutine.create(function()
            while true do
                local x = io.read()
                send(x)
            end
        end)
    end
    
    -- consumer-driven design
    consumer(producer())

    producer产生数据,consumer消费数据,producer与consumer都在各自的协程中完成, 代码很短,但是很难读 - 至少不是那么一目了然,尤其比起这个直接的循环:

    function produce()
        return io.read()
    end
    
    function consume(x)
        print(x)
    end
    
    while true do
        local x = produce()
        consume(x)
    end

    好在哪里?

    书中说可以添加缓存控制速度,或者进行数据过滤 - 但是这在循环版本的producer-consumer中也都能做到,无非在在实现produce是多些逻辑,或者再加个filter的函数处理produce的返回值,协程版本毫无优势可言。

    实在要说其优点,恐怕只是:producer和consumer的代码在各自的协程中实现,并通过resume-yield交换数据 - 实现了松耦合。这是个优点,可以还是不太满意,再往下看时,看到了全排列那个例子,稍作修改,让我比较直观的感觉到了协程这种控制结构的灵活性:

    function send(a)
        coroutine.yield(a)
    end
    
    function receive(prod)
        local status, value = coroutine.resume(prod)
        return value
    end
    
    function consumer(prod)
        local function print_result(a)
            for _, v in ipairs(a) do
                io.write(v, " ")
            end
            io.write('\n')
        end
        
        while true do
            local a = receive(prod)
            if not a then break end
            print_result(a)
        end
    end
    
    
    function producer(a)
        function permgen(a, n)
            if n == 0 then
                send(a) -- send something for consuming from an recursive call
            else
                for i=1, n do
                    a[n], a[i] = a[i], a[n]
                    permgen(a, n-1)
                    a[n], a[i] = a[i], a[n]
                end
            end
        end
    
        local n = table.getn(a)
        return coroutine.create(function() permgen(a, n) end)
    end
    
    consumer(producer({1,2,3,4}))

    这里全排列采用了递归算法:对数组中的每个元素,把它交换到数组末尾,然后对前n-1个元素的子数组做同样的事,当n==0时,输出一个排列。

    这个在produce的时候,因为在一个递归调用中,你无法一个个返回:

    • 要么每找到一个,直接在里面处理 - 这样produce-consume的结构就有点混淆在一起;
    • 或者先把结果保存在一个共享内存中,产生全部排列后,再逐个处理 - 这样要另外开辟空间,流程上感觉也不够简洁。

    于是,协程的有点就显现出来了,不同于简单函数中的return,协程可以在找到一个排列后,yield挂起本协程并返回该排列,返回到原来resume这个协程的代码处,取得数据进行consume,然后继续resume进入协程获取排列 - 通过协程灵活控制流程并传递数据,十分漂亮。

    所以,那个循环版本的问题是:并不是所有produce的数据,都可以简单的return回来的。

  • 相关阅读:
    帐户当前被锁定,所以用户 sa 登录失败。系统管理员无法将该帐户解锁 解决方法
    Web页中table导出到execl(带模板)
    Jquery选择器
    JS 用window.open()函数,父级页面如何取到子级页面的返回值?
    SQL2008:在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。
    划线标注
    Unity与Android间的交互
    ActiveMQ的配置与使用
    OpenCv的Java,C++开发环境配置
    JDBC的超时原理
  • 原文地址:https://www.cnblogs.com/baiyanhuang/p/2775315.html
Copyright © 2020-2023  润新知