【问题标题】:Detect end of TCL background process in a TCL script在 TCL 脚本中检测 TCL 后台进程的结束
【发布时间】:2012-08-24 15:29:15
【问题描述】:

我正在开发一个使用 EXEC 命令来运行 make 文件的程序。这可能需要很长时间,所以我想把它放在后台,这样 GUI 就不会锁定。不过,我还希望禁用 GUI,并且仅在编译 make 文件时才运行进度条。

那么如何在 TCL 中检测后台进度何时完成?

编辑:它变得更加复杂,因为我的老板希望命令窗口保持打开(或可见),以便用户可以看到制作进度并查看它是否出错。

附:搞清楚线程会更容易吗?我需要一些方法来防止 GUI 锁定(防止没有响应)。'

编辑:GUI 是用 TK 制作的。 我认为 TK 是导致问题的单线程。或者可能是它默认为单线程,我想将其设置为多线程。

【问题讨论】:

    标签: user-interface exec tcl background-process


    【解决方案1】:

    正如@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 构建(现在这是所有平台 AFAIK 的默认设置,但是旧版本的 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
    

    【讨论】:

    • 所以我可以执行bgexec [set makeFinish 1] cmd /c start /wait cmd /c make all-all 之类的操作并“监视”makeFinish 更改为 1?
    • bgexec {set makeFinish 1} cmd /c start /wait cmd /c make all-all(注意[] 会直接执行命令)然后观察它(但观察通常是用vwait 完成的——在Tk 中不是一个好主意)或调用其他一些proc更新用户界面。
    • 有没有办法在这个后台进程中捕获错误?如果您提前关闭命令窗口,那么程序将不会知道它已停止。
    • 即使后台进程选择在退出前关闭 stdout (fclose(1)) 也能正常工作吗?
    【解决方案2】:

    您希望在管道中运行 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
    

    我不确定在 Tk 的事件循环中使用 vwait。也许专家会在这里帮助我。

    【讨论】:

    • 我应该使用open?这不是我的程序,所以我不确定我应该改变太多(我只是被要求解除锁定)。 exec 命令是:“exec cmd /c start cmd /c make all-all &” 那么打开会是什么?我想我只是想说我真的不知道两者之间的区别。
    • open "| cmd /c start cmd /c make all-all" r。在 Tk 中,您不需要 vwait,因为 Tk 已经使用事件循环来处理按钮单击等事件。
    • 在Tk内部使用vwait确实是个坏主意;您可能会在嵌套的事件循环中非常纠结(并且您需要注意正确处理应用程序终止以及其他一些事情)。重写代码以处理回调中关闭后发生的情况;这很容易使用事件循环。
    猜你喜欢
    • 1970-01-01
    • 2012-03-05
    • 1970-01-01
    • 2020-12-29
    • 2015-05-21
    • 1970-01-01
    • 2011-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多