0

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.

jjno91
  • 653
  • 7
  • 19
  • 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 Feb 12 '13 at 09:17
  • @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 Feb 13 '13 at 04:10

1 Answers1

2

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".

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
Johannes Kuhn
  • 14,778
  • 4
  • 49
  • 73
  • And the creation of a thread is a very expensive operation, so try to reuse the thread if possible. I always load code/procs in `thread::create` (don't forget to call `::thread::wait` at the end) and just execute a single command (like `fib` in the example) if I need to calculate something. If you need more than one calculation at the same time, consider using tpool (Part of the Thread package, but not as easy to use as "pure" thread) – Johannes Kuhn Feb 12 '13 at 06:55
  • An other thing that I noticed while looking over the code again: You don't have access to the variables of an other Thread; so `set EndWait 1` in the Thread is useless. ofc you can use `::thread::send -async $maintid [list set varname value]` to set a variable in an other thread, but NEVER use this without -async. If the mainthread calls you without -async and you try to execute something in that thread (without -async) you get a deadlock. – Johannes Kuhn Feb 12 '13 at 06:58
  • 1
    You could do `thread::create` without a script (which is just like using `thread::wait` as the script) and `thread::send` the procedure definitions to it. – Donal Fellows Feb 12 '13 at 09:29
  • Okay, sorry about the long time to respond, but I don't work on Tuesdays so I can't look at my code. I do believe that this will fix the script freezing. Any ideas about how to set the EndWait variable from within the script so that `vwait` will work properly? As of now, after the thread completes my script just waits indefinitely since the variable is being set in another scope. – jjno91 Feb 13 '13 at 03:43
  • I'm an idiot, please ignore that last comment. I was reading your answer on the phone and I completely missed what you said about the result variable. Thanks so much for the answer. I'll try it asap at work tomorrow and accept you answer. Cheers! – jjno91 Feb 13 '13 at 03:52
  • again, there is the optional `varname` argument to `thread::send`, the other way with `thread::send -async $tid [list set varname]` as callback almost never needed. – Johannes Kuhn Feb 13 '13 at 15:36