Прерывание цикла while с помощью переменной, а затем использование vwait для ожидания завершения цикла. Не работает?

В настоящее время у меня есть графический интерфейс, который после некоторой автоматизации (с использованием ожидания) позволяет пользователю взаимодействовать с одним из 10 telnet-соединений. Взаимодействие осуществляется с помощью следующего цикла:

#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. Я знаю, что код кажется немного странным/шатким для обработки процессов с двумя переменными, но это была отчаянная последняя попытка заставить это работать.

Мертвый сигнал отправляется правильному процессу (в виде secondDead или firstDead). У меня установлено значение тайм-аута 1 секунда для взаимодействия, так что он вынужден продолжать проверять, является ли цикл while истинным, даже когда пользователь взаимодействует с этим сеансом telnet. После того, как мертвый сигнал отправлен, он ожидает подтверждения того, что процесс умер (через vwait).

Проблема в том, что после отправки сигнала цикл никогда не поймет, что он должен умереть, если ему не будет предоставлен контекст для его проверки. Цикл должен выполняться до тех пор, пока он не будет остановлен first или secondDead. Таким образом, перед переключением на следующий процесс должна быть какая-то форма ожидания, позволяющая циклу в processInteraction предыдущего процесса иметь контроль.

Любая помощь будет принята с благодарностью.


person Greg Kresge    schedule 12.06.2012    source источник
comment
почему строка global killed в killInteractions закомментирована? Вы уверены, что killed правильно устанавливается processInteraction?   -  person bovine    schedule 12.06.2012
comment
Разве vwait автоматически не ищет переменную на глобальном уровне? Она была раньше и все равно не работала. И да, кажется, что kill правильно изменяется в processInteraction. Если он выходит из цикла из-за того, что killInteract установлен , вводится цикл if, который устанавливает kill. Я видел эту работу несколько раз, так как во втором процессе, который я щелкнул, была синтаксическая ошибка. В результате этот процесс немедленно завершился. Цикл первого процесса был введен обратно, и он завершился правильно, (поскольку его было приказано убить, оставив его цикл while преждевременно делать до следующего нажатия кнопки)   -  person Greg Kresge    schedule 12.06.2012
comment
Команда vwait всегда разрешает имена переменных относительно глобального пространства имен; если в них нет ::, они глобальные.   -  person Donal Fellows    schedule 13.06.2012


Ответы (1)


Ваш код кажется мне чрезвычайно сложным. Однако ключевая проблема заключается в том, что вы запускаете внутренние циклы событий (код цикла событий довольно прост, и поэтому вполне предсказуемо представляет собой проблему) и создаете стек C из вещей, которые застряли. Вы не хотите этого!

Давайте начнем с определения того, где находятся эти внутренние циклы событий. Во-первых, vwait — одна из канонических команд цикла обработки событий; он запускает цикл событий до тех пор, пока его переменная не будет установлена ​​(предположительно, сценарием события). Однако это не единственный случай. В частности, Expect interact также запускает цикл обработки событий. Это означает, что все может стать вложенным и запутанным и… ну, вам это не нужно. (На этой странице говорится о update, но это относится ко всем вложенным цикл событий.) Помещение цикла событий внутри вашего собственного while особенно вероятно приведет к головной боли при отладке.

Лучший способ исправить это — переписать код, чтобы использовать стиль передачи продолжения. Вместо того, чтобы писать код с вложенными циклами событий, вы перестраиваете вещи так, чтобы у вас были фрагменты кода, которые оцениваются по событиям и передают необходимое состояние между ними без запуска вложенного цикла событий. (Если вы не используете Expect и используете Tcl 8.6, я бы посоветовал использовать coroutine для этого, но я не думаю, что это работает с Expect в настоящее время, и для этого требуется бета-версия Tcl, которая еще не получила широкого распространения.)

Увы, все усложняется необходимостью взаимодействия с подпроцессами. Нет никакого способа взаимодействовать в фоновом режиме (и в этом нет особого смысла). Вместо этого вам нужно использовать только один interact во всей вашей программе и заставить его переключаться между порождаемыми соединениями. Вы делаете это, давая параметру -i имя глобальной переменной, которая содержит текущий идентификатор для взаимодействия, вместо непосредственного идентификатора. (Это «косвенный» идентификатор спавна.) Я думаю, что самый простой способ заставить эту работу работать — это иметь «не связанный ни с чем другим» идентификатор спавна (например, вы подключаете его к cat >/dev/null просто для того, чтобы действовать как ничего не делающий). ), которые вы создаете в начале своего скрипта, а затем заменяете реальное соединение, когда это имеет смысл. Реальные вещи, которые вы сейчас используете interact, лучше всего делать с expect_background (не забудьте использовать expect_out вместо interact_out).

Ваш код слишком длинный, чтобы я мог его переписать, но вам следует очень внимательно изучить логику предложения eof в interact; ему нужно сделать больше, чем он делает в данный момент. Код для уничтожения из графического интерфейса также должен быть изменен; он должен быть send подходящим маркером EOF для порожденных процессов, которые должны быть уничтожены, и не ждать подтверждения смерти.

person Donal Fellows    schedule 13.06.2012