Interrumpir un ciclo while por una variable, luego usar vwait para esperar a que el ciclo muera. ¿No funciona?

Actualmente tengo una GUI, que después de cierta automatización (usando esperar) permite al usuario interactuar con una de las 10 conexiones de telnet. La interacción se realiza mediante el siguiente bucle:

#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
    }
}

Cuando se selecciona un nuevo proceso, el anterior debe eliminarse. Anteriormente lo tenía donde si se hace clic en un botón, simplemente ingresa a este ciclo nuevamente. Eventualmente me di cuenta de que los bucles while nunca se cerraban, y después de presionar 124 botones, falla (stackoverflow =P). No se ejecutan en segundo plano, pero están en la pila. Así que necesitaba una forma de matar el bucle en el processInteraction función cuando se inicia un nuevo proceso. Aquí está mi último intento de solución después de muchos fracasos:

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 se llama cuando se presiona un botón. El guión se aferra vwait. Sé que el código parece un poco extraño/torpe para manejar procesos con dos variables, pero este fue un último esfuerzo desesperado para que esto funcionara.

Se envía una señal muerta al proceso correcto (en forma de secondDead or firstDead). Tengo el valor de tiempo de espera establecido en 1 segundo para la interacción, por lo que se ve obligado a seguir comprobando si el while loop es verdadero, incluso mientras el usuario está interactuando con esa sesión de telnet. Una vez que se envía la señal muerta, espera la confirmación de que el proceso ha muerto (a través de vwait).

El problema es que una vez que se envía la señal, el bucle nunca se dará cuenta de que debe morir a menos que se le dé el contexto para verificarlo. El ciclo debe ejecutarse hasta que sea expulsado por first or secondDead. Por lo tanto, debe haber algún tipo de espera antes de cambiar al siguiente proceso, lo que permite que el ciclo entre processInteraction del proceso anterior para tener el control.

Cualquier ayuda sería muy apreciada.

preguntado el 12 de junio de 12 a las 17:06

porque es el global killed línea en killInteractions comentada? Está seguro killed ¿Está siendo configurado correctamente por processInteraction? -

¿Vwait no busca automáticamente la variable a nivel global? La tenía antes y no funcionó de todos modos. Y sí, parece que kill se está cambiando correctamente en processInteraction. , se ingresa el bucle if que establece la muerte. He visto que esto funciona varias veces, ya que el segundo proceso en el que hice clic tenía un error de sintaxis. Como resultado, ese proceso finalizó de inmediato. Se volvió a ingresar al ciclo while del primer proceso y se cerró correctamente. (ya que se ordenó que lo mataran dejando su bucle while antes de tiempo para la siguiente pulsación del botón) -

La vwait el comando siempre resuelve los nombres de las variables con respecto al espacio de nombres global; si no tienen :: en, son globales. -

1 Respuestas

Tu código me parece extremadamente complicado. Sin embargo, el problema clave es que está ejecutando bucles de eventos internos (el código del bucle de eventos es bastante simple, por lo que es un problema predecible) y está construyendo la pila C con cosas que están atascadas. ¡No quieres eso!

Comencemos identificando dónde están esos bucles de eventos internos. Primeramente, vwait es uno de los comandos de bucle de eventos canónicos; ejecuta un bucle de eventos hasta que se establece su variable (por un script de eventos, presumiblemente). Sin embargo, no es el único. En particular, Expect interact Además, ejecuta un bucle de eventos. Esto significa que todo se puede anidar y enredar y... bueno, tu no quieres eso. (Esa página habla de update, pero se aplica a todos los bucles de eventos anidados). Poner un bucle de eventos dentro de su propio while es particularmente probable que provoque dolores de cabeza en la depuración.

La mejor ruta para arreglar esto es reescribir el código para usar el estilo de paso de continuación. En lugar de escribir código con bucles de eventos anidados, en su lugar, reorganiza las cosas para que tenga fragmentos de código que se evalúen en eventos y que pasen el estado que sea necesario entre ellos. sin iniciar un bucle de eventos anidados. (Si no estaba usando Expect y estaba usando Tcl 8.6, le aconsejo usar coroutine para hacer esto, pero no creo que funcione con Expect actualmente y requiere una versión beta de Tcl que aún no está ampliamente implementada).

Por desgracia, todo se complica más por la necesidad de interactuar con los subprocesos. No hay forma de interactuar en segundo plano (ni tiene mucho sentido). En cambio, lo que debe hacer es usar exactamente uno interact en todo su programa y hacer que cambie entre las conexiones generadas. Lo haces dando el -i opción el nombre de una variable global que contiene la identificación actual para interactuar, en lugar de la identificación directamente. (Esta es una identificación de generación "indirecta"). Creo que la forma más fácil de hacer que esto funcione es tener una identificación de generación "no conectada a nada más" (por ejemplo, lo conecta a cat >/dev/null solo para actuar como un no hacer nada) que hace al comienzo de su secuencia de comandos, y luego cambia la conexión real cuando tiene sentido. Las cosas reales que usas actualmente interact a tener en cuenta es mejor hacerlo con expect_background (recuerde usar expect_out en lugar de interact_out).

Su código es demasiado largo para que lo reescriba, pero lo que debe hacer es mirar con mucho cuidado la lógica del eof cláusula de la interact; necesita hacer más de lo que hace en este momento. El código para matar desde la GUI también debe cambiarse; debería send un marcador EOF adecuado para el (los) proceso (s) generado (s) que se eliminará y No espera para que se confirme la muerte.

Respondido el 13 de junio de 12 a las 09:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.