Обнаружение завершения фонового процесса TCL в сценарии TCL

Я работаю над программой, которая использует команду EXEC для запуска файла make. Это может занять много времени, поэтому я хочу поместить его в фоновый режим, чтобы графический интерфейс не блокировался. Однако я также хочу, чтобы графический интерфейс был отключен, а индикатор выполнения запускался только во время компиляции файла make.

Итак, как я могу определить, когда фоновый процесс завершился в TCL?

Редактировать: это усложняется, потому что мой босс хочет, чтобы окно команд оставалось открытым (или было видимым), чтобы пользователь мог видеть ход выполнения и видеть, есть ли ошибки.

P.S. Будет ли проще разобраться с потоками? Мне нужен какой-то способ предотвратить блокировку графического интерфейса (предотвратить НЕ ОТВЕЧИВАНИЕ).'

Изменить: графический интерфейс создан с помощью TK. Я думаю, что TK является однопоточным, что вызывает проблему. Или может быть, что по умолчанию он однопоточный, а я хочу установить многопоточный.


person Community    schedule 24.08.2012    source источник


Ответы (2)


Как отметил @glenn-jackman, предпочтительнее использовать fileevent (потому что он должен работать везде).

proc handle_bgexec {callback chan} {
    append ::bgexec_data($chan) [read $chan]
    if {[eof $chan]} {
        # end of file, call the callback
        {*}$callback $::bgexec_data($chan)
        unset ::bgexec_data($chan)
    }
}

proc bgexec {callback args} {
    set chan [open "| $args" r]
    fconfigure $chan -blocking false
    fileevent $chan readable [list handle_bgexec $callback $chan]
    return
}

Вызовите это как bgexec job_done cmd /c start /wait cmd /c make all-all. job_done вызывается с выводом команды после ее завершения.

Для этого также можно использовать потоки, но для этого требуется многопоточная сборка tcl (которая сейчас используется по умолчанию для всех платформ, насколько я знаю, но более старые версии Tcl, особенно под unix, по умолчанию не создают многопоточный Tcl.) и пакет Thread (который включен по умолчанию). Подход к использованию его с потоками будет следующим:

thread::create "[list exec cmd /c start /wait cmd /c make all-all];[list thread::send [thread::id] {callback code}];thread::exit"

Если вам нужно вызывать это на регулярной основе, возможно, стоит использовать только один рабочий поток вместо создания нового для каждого задания.

Изменить: добавьте /wait в качестве параметра для запуска, чтобы первый cmd продолжал работать.

cmd /c start /wait cmd /c make all-all
person Johannes Kuhn    schedule 24.08.2012
comment
Итак, я мог бы сделать что-то вроде bgexec [set makeFinish 1] cmd /c start /wait cmd /c make all-all и посмотреть, как makeFinish изменится на 1? - person ; 27.08.2012
comment
bgexec {set makeFinish 1} cmd /c start /wait cmd /c make all-all (обратите внимание, что [] выполнит команду напрямую), а затем просмотрите ее (но наблюдение обычно выполняется с помощью vwait - не очень хорошая идея в Tk) или вызовите какой-либо другой процесс, который обновляет пользовательский интерфейс. - person Johannes Kuhn; 27.08.2012
comment
Есть ли способ отловить ошибки в этом фоновом процессе? Если вы закроете командное окно раньше, программа не узнает, что она остановлена. - person ; 29.08.2012
comment
Это работает, даже если фоновый процесс решил закрыть стандартный вывод (fclose(1)) перед выходом? - person Andreas; 23.01.2019

Вы хотите запустить процесс make в конвейере и использовать цикл событий и fileevent для отслеживания его хода (см. http://wiki.tcl.tk/880)

proc handle_make_output {chan} {
    # The channel is readable; try to read it.
    set status [catch { gets $chan line } result]
    if { $status != 0 } {
        # Error on the channel
        puts "error reading $chan: $result"
        set ::DONE 2
    } elseif { $result >= 0 } {
        # Successfully read the channel
        puts "got: $line"
    } elseif { [chan eof $chan] } {
        # End of file on the channel
        puts "end of file"
        set ::DONE 1
    } elseif { [chan blocked $chan] } {
        # Read blocked.  Just return
    } else {
        # Something else
        puts "can't happen"
        set ::DONE 3
    }
}

set chan [open "|make" r]
chan configure $chan -blocking false
chan event $chan readable [list handle_make_output $chan]
vwait ::DONE
close $chan

Я не уверен в использовании vwait в цикле событий Tk. Возможно, здесь мне поможет специалист.

person glenn jackman    schedule 24.08.2012
comment
Я должен использовать open? Это не моя программа, поэтому я не уверен, что должен слишком сильно меняться (меня просто попросили снять блокировку). Команда exec: exec cmd /c start cmd /c make all-all и так что бы это было с открытым? Думаю, я просто хочу сказать, что на самом деле не знаю разницы между ними. - person ; 24.08.2012
comment
open "| cmd /c start cmd /c make all-all" r. В Tk вам не нужен vwait, потому что Tk уже использует цикл событий для таких событий, как нажатие кнопки. - person Johannes Kuhn; 25.08.2012
comment
Это действительно плохая идея использовать vwait внутри Tk; вы можете очень сильно запутаться во вложенных циклах событий (и вам нужно следить за правильной обработкой завершения приложения и кучей других вещей). Перепишите свой код, чтобы вместо этого иметь дело с тем, что происходит после закрытия в обратном вызове; это легко сделать с циклом событий. - person Donal Fellows; 25.08.2012