0
votes

I need to be able to set the variable to end a vwait from inside a thread. This is because I have a loop that normally locks the interperator and therefore any GUI it is running until it completes.

I'm wanting it to function just like a sleep command like this:

global EndSleep
after ${SleepTime_ms} set EndSleep 1
vwait EndSleep

Only I need to set the vwait variable when a while loop that is querying a device exits.

Here is my code currently:

    proc ::VISA::Wait {VisaAlias} {
    # Link global variable
    global EndWait

    # Execute commands in a thread so the GUI is not locked while executing
    set Thread [thread::create]
    thread::send ${Thread} [list set VisaAlias ${VisaAlias}]
    thread::send ${Thread} {
        source MolexVisa.tcl

        # Temporarily make new connection with device
        VISA::Connect ${VisaAlias}

        # Query operation complete bit
        VISA::Query ${VisaAlias} "*OPC?"

        # Continue to attempt to read OPC bit until any response is given; typically 1
        while {[string equal [VISA::Read ${VisaAlias}] ""]} {}

        # Destroy temporary connection
        VISA::Disconnect ${VisaAlias}

        # Set vwait variable
        set EndWait 1
    }

    # Wait for thread to end
    vwait EndWait

    # Mark thread for termination
    thread::release ${Thread}
}

Currently the thread is still freezing the GUI. Also, since the variable in the thread and the variable I'm expecting aren't the same, obviously it just waits forever.

Any advice or help is appreciated. I believe I've exhausted all other more practical ways of accomplishing the task, but some more insight is welcomed.

1
It's a shame that VISA::Read is synchronous or you could (probably) do it without a thread. If it is quick to respond (but often returns empty) then periodic polling — such as once every few hundred ms — does well, and you can use Tcl 8.6's coroutines to make everything nicely behaved.Donal Fellows
@DonalFellows unfortunately the query I'm putting into the device *OPC? causes it to freeze the channel until all of it's operations are done. Now this is where it's usefulness comes from, but also as I'm sure you see it forces the subsequent reads to timeout until it is complete. I thought about lowering to the timeout on the channel to a hundred ms or so, but ideally having zero lag time is desirable for this application. So, I'm hoping the thread will get close to zero lag time before the 'vwait'.jjno91

1 Answers

2
votes

use the -async flag for thread::send. By default, ::thread::send blocks until the script has been executed (which defeats most of the time the use of threads).

If you use the -async flag, you can also use the optional variable argument to thread::send and vwait for that, e.g.

set tid [thread::create {
    proc fib n {
        if {$n == 0 || $n == 1} {return 1}
        return [expr {[fib [expr {$n - 1}]] + [fib [expr {$n - 2}]]}]
    }
    thread::wait
}]
::thread::send -async $tid [list fib $num] result
vwait result
::thread::release $tid
# ... do something with result

This should prevent the GUI from freezing. Note that this implementation of the fibonacci is not the best and is a placeholder for "some expensive calculation".