2

私は現在 GUI を持っています。自動化 (expect を使用) の後、ユーザーは 10 個の telnet 接続の 1 つと対話できるようになります。相互作用は、次のループを使用して行われます。

#After selecting an item from the menu, this allows the user to interact with that process
proc processInteraction {whichVariable id id_list user_id} {
    if {$whichVariable == 1} {
        global firstDead
        set killInteract $firstDead
    } elseif {$whichVariable == 2} {
        global secondDead
        set killInteract $secondDead
    }
    global killed

    set totalOutput ""
    set outputText ""
    #set killInteract 0
    while {$killInteract == 0} {
        set initialTrue 0
        if {$whichVariable == 1} {
            global firstDead
            set killInteract $firstDead
        } elseif {$whichVariable == 2} {
            global secondDead
            set killInteract $secondDead
        }
        puts "$id: $killInteract"
        set spawn_id [lindex $id_list $id]
        global global_outfile

        interact  {
            -i $spawn_id
            eof {
                set outputText "\nProcess closed.\n"
                lset deadList $id 1
                puts $outputText
                #disable the button
                disableOption $id $numlcp
                break
            }

            -re (.+) {
                set outputText $interact_out(0,string)
                append totalOutput $outputText
                #-- never looks at the following string as a flag
                send_user -- $outputText
                #puts $killInteract
                continue
            }

            timeout 1 {
                puts "CONTINUE"
                continue
            }        
        }
    }
    puts "OUTSIDE"
    if {$killInteract} {
        puts "really killed in $id"
        set killed 1
    }
}

新しいプロセスが選択されると、前のプロセスは強制終了されます。以前は、ボタンをクリックすると、このループに再び入るだけでした。最終的に、while ループが終了せず、ボタンを 124 回押すとクラッシュする (stackoverflow =P) ことに気付きました。それらはバックグラウンドで実行されていませんが、スタック上にあります。processInteractionそのため、新しいプロセスが開始されたときに関数内のループを強制終了する方法が必要でした。多くの失敗の後、解決策を試みた最後の試みは次のとおりです。

proc killInteractions {} {
    #global killed
    global killInteract
    global first
    global firstDead
    global secondDead
    global lastAssigned

    #First interaction
    if {$lastAssigned == 0} {
        set firstDead 0
        set secondDead 1
        set lastAssigned 1
        #firstDead was assigned last, kill the first process
    } elseif {$lastAssigned == 1} {
        set firstDead 1
        set secondDead 0
        set lastAssigned 2
        vwait killed
        #secondDead was assigned last, kill the second process
    } elseif {$lastAssigned == 2} {
        set secondDead 1
        set firstDead 0
        set lastAssigned 1
        vwait killed
    }

    return $lastAssigned
}

killInteractionsボタンが押されたときに呼び出されます。スクリプトがハングアップしvwaitます。2 つの変数を持つプロセスを処理するためのコードが少し奇妙/不安定に見えることはわかっていますが、これはこれを機能させるための必死の最後の努力でした。

デッド シグナルが正しいプロセスに送信されます (secondDeadまたはの形式でfirstDead)。対話のタイムアウト値を 1 秒に設定しているためwhile、ユーザーが telnet で接続されたセッションと対話している間でも、ループが true かどうかをチェックし続ける必要があります。dead シグナルが送信されると、プロセスが終了したことの確認を待ちます ( を介してvwait)。

問題は、シグナルが送信されると、それを確認するためのコンテキストが与えられない限り、ループは終了する必要があることに決して気付かないことです。firstループは、またはによって追い出されるまで実行する必要がありますsecondDead。したがって、次のプロセスに切り替える前に何らかの形で待機する必要がありprocessInteraction、前のプロセスのループインが制御できるようになります。

どんな助けでも大歓迎です。

4

1 に答える 1

0

あなたのコードは私には非常に複雑に思えます。ただし、重要な問題は、内部イベント ループを実行していて (イベント ループ コードは非常に単純なので、問題になることが予想されます)、動かなくなったもので C スタックを構築していることです。あなたはそれをしたくない !

これらの内部イベント ループがどこにあるかを特定することから始めましょう。まず、vwait正規のイベント ループ コマンドの 1 つです。変数が設定されるまで (おそらくイベント スクリプトによって)、イベント ループが実行されます。しかし、それだけではありません。特に、Expectinteract イベント ループを実行します。これは、すべてが入れ子になったり絡み合ったりする可能性があることを意味します。(そのページでは について説明updateしていますが、ネストされたすべてのイベント ループに適用されます。) イベント ループを独自の内部に配置するwhileと、デバッグの頭痛の種になる可能性が特に高くなります。

これを修正する最善の方法は、継続渡しスタイルを使用するようにコードを書き直すことです。ネストされたイベント ループを使用してコードを記述する代わりに、イベントで評価され、ネストされたイベント ループを開始せずにイベント間で必要に応じて状態を渡すコードを作成するように、物事を再配置します。(Expect を使用しておらず、Tcl 8.6 を使用coroutineしている場合は、これを使用することをお勧めしますが、現在の Expect では機能しないと思います。また、まだ広く展開されていないベータ版の Tcl が必要です。 .)

残念ながら、サブプロセスとやり取りする必要があるため、すべてがより複雑になります。バックグラウンドで対話する方法はありません (また、あまり意味がありません)。代わりにinteract、プログラム全体で 1 つだけを使用し、生成された接続を切り替える必要があります。-iこれは、id を直接指定する代わりに、対話する現在の ID を保持するグローバル変数の名前をオプションに指定することによって行います。(これは「間接的な」スポーン ID です。) これを機能させる最も簡単な方法は、「他の何にも接続されていない」スポーン ID を持つことだと思います (たとえば、何もしないように接続するcat >/dev/nullだけです)。スクリプトの開始時に作成し、意味があるときに実際の接続にスワップします。現在実際に使用しているモノinteract注意することは、(の代わりにexpect_background使用することを忘れないでください)で行うのが最適です。expect_outinteract_out

あなたのコードは私が書き直すには長すぎますが、あなたがすべきことは、 ;のeof句のロジックを注意深く調べることです。interact現時点よりも多くのことを行う必要があります。GUI から kill するコードも変更する必要があります。生成さsendれたプロセスへの適切な EOF マーカーが強制終了され、死亡が確認されるのを待たない必要があります。

于 2012-06-13T08:26:22.687 に答える