0

私はluaコルーチン(lua 5.1)を使用して、アプリケーションのプラグインシステムを作成しています。プラグインが処理フレームごとに1回生成される別個のアプリケーションプログラムであるかのように動作できるように、コルーチンを使用したいと考えていました。プラグインプログラムは通常、次のような式に従います。

function Program(P)
    -- setup --
    NewDrawer(function()
        -- this gets rendered in a window for this plugin program --
        drawstuff(howeveryouwant)
    end)
    -- loop --
    local continue = true
    while continue do
        -- frame by frame stuff excluding rendering (handled by NewDrawer) --
        P = coroutine.yield()
    end
end

各プラグインは、フレームごとに1回、アプリケーションのメインループで再開されます。次に、描画が開始されると、各プラグインには、NewDrawerに渡された関数が実行されるときに描画する個別のウィンドウがあります。

このようなもの:

while MainContinue do
    -- other stuff left out --
    ExecutePluginFrames() -- all plugin coroutines resumed once

    BeginRendering()
    -- other stuff left out --
    RenderPluginWindows() -- functions passed to NewDrawer called.
    EndRendering()
end

しかし、レンダリングでエラーが発生するたびに、これが突然奇妙に動作し始め、それ以外の場合は堅牢なエラー処理システムを台無しにしていることがわかりました。何が起こっているのか頭を悩ませるのに少し時間がかかりましたが、メインスレッドの呼び出しスタックにあると予想していたWIN:Draw()の呼び出し(メインアプリケーションによって処理されるため)が実際に原因であったようですコルーチンのコールスタックへの暗黙のジャンプ。

最初の問題は、プログラムが突然終了し、有用なエラー出力がないことでした。次に、プラグインプログラムで定義されたレンダリング関数のスタックトレースバックを調べた後、メインスレッドからウィンドウの描画に至るまでのすべてがそこになく、そのyieldが呼び出しスタックにあることがわかりました。

ウィンドウはスレッドと描画関数で作成されたため、そのスレッドの呼び出しスタックによって処理されているようです。これは、メインスレッドで設定されたpcallの外部にあることを意味するため問題です。

これは起こると思いますか?それはCソースのバグ/ショートカットの結果ですか?私は何か間違ったことをしているのですか、それとも少なくとも正しくは不十分ですか?これをきれいに処理する方法はありますか?

4

2 に答える 2

0

あなたが説明している効果を再現することはできません。これは私が実行しているコードです:

local drawer = {}
function NewDrawer(func)
  table.insert(drawer, func)
end

function Program(P)
    NewDrawer(function()
        print("inside program", P)
    end)
    -- loop --
    local continue = true
    while continue do
        -- frame by frame stuff excluding rendering (handled by NewDrawer) --
        P = coroutine.yield()
    end
end

local coro = coroutine.create(Program)
local MainContinue = true
while MainContinue do
    -- other stuff left out --
    -- ExecutePluginFrames() -- all plugin coroutines resumed once
    coroutine.resume(coro, math.random(10))
    -- RenderPluginWindows() -- functions passed to NewDrawer called.
    for _, plugin in ipairs(drawer) do
      plugin()
    end
    MainContinue = false
end

コードをステップスルーしてスタックを見ると、NewDrawerに設定されているコールバックが「メイン」スレッドで呼び出されます。coroutine.running()現在のスレッドを返す呼び出しを行うかnil、メインスレッド内にいる場合は、自分で確認できます。

于 2013-01-21T01:27:55.800 に答える
0

私の場合、なぜこれが起こっているのかを発見しました。NewDrawerに渡された関数を呼び出したレンダリングオブジェクトは、作成時に(cコードによって)作成されたlua状態へのポインターで初期化され、これは関連するluaデータへのアクセスと描画関数の呼び出しに使用されます。lua_Stateとコルーチンの関係は見たことがありませんでした。したがって、Cコードが原因で関数が発生している場合は、yield後に関数がスタックで呼び出される可能性があります。

解決策に関しては、プログラムを2つのコルーチンに分割することにしました。1つはレンダリング用、もう1つは処理用です。これにより、レンダリングオブジェクトの作成スレッドが呼び出しスレッドにもなり、レンダリングループと処理ループの独立性という優れた利点が維持されるため、問題が修正されます。

于 2013-01-21T21:53:39.893 に答える