【问题标题】:Stop a TCL script from freezing while processing a command在处理命令时停止 TCL 脚本冻结
【发布时间】:2013-01-09 20:26:12
【问题描述】:

好的,所以我在高层次上做的是扫描系统以查找连接到它的所有 VISA 设备并让它们识别自己。

问题在于,并非所有 VISA 设备都支持识别自己的功能,而我知道的唯一方法是告诉设备这样做。这迫使那些无法识别自己的人依赖至少 1 秒的超时。在等待超时时,我的 TCL 脚本和 Wish 应用程序冻结,直到超时完成。对于多台设备,这让我的等待时间很尴尬,可能长达几秒钟,我无法向用户更新正在发生的事情。

这是我的代码:

proc ::VISA::Scan {} {
    # Open a temporary resource manager
    set TemporaryResourceManagerId [::visa::open-default-rm]

    # Get addresses for all devices on system
    foreach address [::visa::find $TemporaryResourceManagerId "?*"] {

        # Create temporary VISA channel
        set TemporaryChannel [visa::open $TemporaryResourceManagerId $address]

        # Have device identify itself while suppressing errors
        if {![catch {puts $TemporaryChannel "*IDN?"}]} {
            if {![catch {gets $TemporaryChannel} result]} {
                if {![string is space $result]} {
                    puts $address
                    puts "$result \n"
                }

                # Clear any potential errors
                puts $TemporaryChannel "*CLS"
            }
        }

        # Destroy temporary channel
        close $TemporaryChannel
        unset TemporaryChannel
    }

    # Destroy temporary resource manager
    close $TemporaryResourceManagerId
    unset TemporaryResourceManagerId
}

我想知道是否有办法在 TCL 方面防止这种情况发生,因为我无法知道我将查询哪些类型的设备。我尝试在脚本的几个不同位置使用“update”和“update idletasks”,但它只是让我在冻结之间有一点时间。

任何帮助将不胜感激。提前致谢!

【问题讨论】:

    标签: tcl progress visa


    【解决方案1】:

    通过将 I/O 通道设置为非阻塞并使用 fileeventchan event 来使用 tcl 的事件循环的标准方法;但是,tclvisa 文档指出,签证通道不支持 fileevent。

    因此,下一个最好的方法是使用非阻塞 I/O(它只是将超时设置为 0)并使用 busyloop 读取通道或在延迟后读取它;其中任何一个都应该通过事件循环来处理,而不是在周围撒上update(这会产生不良的副作用)。

    所以对于busyloop,你可以这样做:

    proc busyread {v n} {
        if {$::readdone == 1} {set ::$n "Error"}    
        set r [visa::read $v]
        if {$r == ""} {
            after 5 [list busyread $v $n]
        } else {
            set ::$n $r
            set ::readdone 1
        }
    }
    
    
    set f [visa::open ...]
    fconfigure $f -blocking 0
    after 1000 [list set ::readdone 1]
    set ::readdone 0
    busyread $f result
    vwait ::readdone   
    # $result will now be either the result, or "Error"
    

    这会不断重新安排read,只要它一直空着回来。

    这将需要重新构建以在更大的 gui 程序中工作(vwait 和超时需要以不同的方式完成),但这显示了基本方法。

    【讨论】:

    • 这实际上不是我解决问题的方式(检查我的答案),但我相信这是解决我计划在未来版本中升级我的代码的问题的更好方法。我接受这个答案是因为它解决了 TCL 而不是 tclvisa 的问题,而且由于我在我的问题中没有提到 tclvisa,我认为这个答案对于其他有类似问题的用户来说比我自己的答案更有用。
    【解决方案2】:

    您必须使用afterfileevent 来异步处理超时。这并不容易,尤其是在 Tcl8.6 之前的版本中:您必须将一个过程拆分为一堆事件处理程序,并将所有必要的信息传递给它们。

    安排一个超时处理程序:

    proc handleTimeout {channel} {
        ....
        close $channel
        .... # or do some other thing, 
        .... # but don't forget to remove fileevent handler if not closing!
    }
    ....
    after 1000 [list handleTimeout $TemporaryChannel]
    

    使通道非阻塞,安装文件事件处理程序:

    proc tryGetsIDN {channel} {
        if {[gets line]!=-1} {
            # We have an answer!
            # Cancel timeout handler
            after cancel [list handleTimeout $TemporaryChannel]
            ....
        }
    }
    ....
    fconfigure $TemporaryChannel -blocking 0
    fileevent $TemporaryChannel readable [list tryGetsIDN $TemporaryChannel]
    

    最难的部分:确保正确处理 GUI 事件,例如如果有一个“取消”按钮来取消所有异步处理程序,请确保关闭通道并取消超时处理程序(此处可能需要额外记录通道和处理程序)。

    使用 Tcl 8.6,您可以使用协程使您的过程作为协作的“后台线程”工作:很容易实现“超时获取”,它从协程产生并在完成或超时时重新进入它。不过,目前还没有开箱即用的解决方案。

    【讨论】:

    • @evilotto 指出(因为我以前不知道)我正在使用的包 tclvisa 不支持其频道上的 fileevent 命令
    • 好吧,你可以使用线程(如果 tclvisa 东西与线程构建兼容)。在 TCL 中,线程在单独的解释器中运行,thread::send 用于通信(以及其他一些方式)......再次不平凡,但可能。
    • 我对 TCL 比较陌生,但我用作参考的书说,在 TCL 线程中使用扩展是不安全的,而 tclvisa 是一个 C 扩展。有什么想法,或者我应该避免吗?
    • 有些扩展确实不安全,尤其是。如果他们有全局状态。即使使用不安全的扩展,也可以在单个线程中使用它(这可能是您的 GUI 线程之外的另一个线程)。如果我对 tclvisa 有更多了解,我会更有信心地回答。
    【解决方案3】:

    我实际上在我的问题的 tclvisa 方面找到了解决方案。我找到了一种更好的方法来指定通道的超时时间,而不是使用我错误地认为必须使用的内置 tclvisa 命令。

    fconfigure $TemporaryChannel -timeout 100
    

    设置此超时并不能完全解决问题,但可以将其减少到晦涩难懂的程度。感谢大家的回复!

    【讨论】:

      猜你喜欢
      • 2022-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-17
      • 2015-05-08
      • 2010-09-19
      • 2015-10-10
      • 2019-08-14
      相关资源
      最近更新 更多