fiber.yield() 和 fiber.testcancel() 的 Tarantool 纤维行为

Tarantool fiber behavior with fiber.yield() and fiber.testcancel()

我 运行 在构建基于纤程的 Ta运行 工具应用程序时出现意外行为。

我的代码的简单复制器如下所示:

local log = require('log')
local fiber = require('fiber')

box.cfg{}

local func = function()
    for i = 1, 100000 do
        if pcall(fiber.testcancel) ~= true then
            return 1
        end

        fiber.yield()
    end

    return 0
end

local wrapfunc = function()
    local ok, resp = pcall(func)
    log.info(ok)
    log.info(resp)
end

for _ = 1, 100 do
    local myfiber = fiber.create(wrapfunc)
    fiber.sleep(0.02)
    fiber.kill(myfiber)
end

并打印到日志 false, fiber is cancelled。此外,如果我使用以下 func:

local func = function()
    for i = 1, 100000 do
        if pcall(fiber.testcancel) ~= true then
            return 1
        end

        pcall(fiber.yield)
    end

    return 0
end

它打印到日志 true, 1,如果我使用

local func = function()
    for i = 1, 100000 do
        if pcall(fiber.testcancel) ~= true then
            return 1
        end

        if pcall(fiber.yield) ~= true then
            return 2
        end
    end

    return 0
end

它打印到日志 true, 2

我预计在从 运行 myfiber 屈服后,如果控制 returns 到外部光纤并调用 fiber.kill(myfiber),下一次控制 returns 到取消的 myfiber 我们将处于循环迭代的末尾,并且在下一次迭代代码将成功 return 1。但是,func 的工作以抛出错误结束 fiber is cancelled,而不是 return。那么高产纤维的真实生命周期是如何运作的呢?

实际上,这里并没有意外的行为。我相信这主要是文档问题。让我解释。我稍微简化了您的示例:

#!/usr/bin/env tarantool

local fiber = require('fiber')

local f1 = function() fiber.yield() end
local f2 = function() pcall(fiber.yield) end

local func = function(fn)
    fn()
    if not pcall(fiber.testcancel) then
        return 'fiber.testcancel() failed'
    end
end

local fiber1 = fiber.create(function() print(pcall(func, f1)) end)
fiber.kill(fiber1)
local fiber2 = fiber.create(function() print(pcall(func, f2)) end)
fiber.kill(fiber2)

输出将是:

false   fiber is cancelled
true    fiber.testcancel() failed

当您调用 fiber.killfiber.yield()fiber.sleep() 时只会引发错误,因此您的光纤无法到达 fiber.testcancel 并且会挂掉。当您执行 pcall(fiber.yield) 时,您基本上会抑制此错误并继续。然后 fiber.testcancel 检查其纤程状态并重新引发异常。但这是一个愚蠢的例子。

现在,随着代码块越来越大,当涉及到大量函数调用时,您通常希望在 yield 期间捕获这些错误,做一些完成工作并调用 fiber.testcancel() 向上提升错误(想象在大堆栈跟踪的不同部分进行多次此类检查)。我相信这是基本用例,fiber.testcancel 被引入是为了讨论它的设计是否可用。

P.s。是的,没有记录此类 yield 调用的偶尔异常。至少我在 fiber 页面

中找不到任何东西