How can one Tcl thread cause an event in another?

前端 未结 2 1836
故里飘歌
故里飘歌 2021-01-21 10:01

I have a system with multiple I/O interfaces, and I\'m collecting the output of all of them into a common log. Two of the interfaces are through well-behaved channels that are

相关标签:
2条回答
  • 2021-01-21 10:38

    Firstly, the main thread needs to run the event loop in order to receive events. The idiomatic way to do this is to use vwait forever once you've finished setting up your program (I assume you're not going to write to that variable) but if you are running Tk you already have an event loop (GUIs need event loops).

    There are two ways to do the messaging between threads.

    Thread events

    The thread::send command uses events to dispatch code to execute (the message) between threads. All you need to do is to tell the worker thread what the main thread's ID is so it knows where to send to. Note that you may well want to send the event asynchronously, like this:

    thread::send -async $mainID [list eventReceiver "something happened" $payload]
    

    Pipelines

    If you're using Tcl 8.6, you can use chan pipe to create an unnamed OS pipeline. You can then use normal fileevents, etc., to deliver information from thread to another that way.

    # In master
    lassign [chan pipe] readSide writeSide
    thread::transfer $worker $readSide
    thread::send $worker [list variable pipe $readSide]
    fconfigure $writeSide -blocking 0
    fileevent $writeSide readable [list handleLine $writeSide]
    
    # In worker
    fconfigure $pipe -blocking 0 -buffering line
    puts $pipe "got event: $payload"
    

    It's probably easier to use thread events in retrospect! (The main advantage of a pipe is that you can also put the worker in another process if necessary.)

    0 讨论(0)
  • 2021-01-21 10:54

    I finally grokked what Donal was saying about Thread events. I blame inadequate morning caffeine for not getting it the first time.

    All the prior examples of thread::send I've seen concerned a master sending scripts down to the worker thread. In this case, the worker thread needs to send scripts back up to the master, where the script is the one that would be called on [fileevent readable] if this was a channel.

    Here's my test code for the interaction:

    proc reader {payload} {
        puts $payload
    }
    
    set t1 [thread::create]
    thread::send -async $t1 {
        proc produce {parentid} {
            while 1 {
                after 250   ;# substitutes for
                incr data   ;# the blocking read
                thread::send $parentid "reader $data"
            }
        }
    }
    
    set tid [thread::id]
    thread::send -async $t1 [list produce $tid]
    
    vwait forever
    

    The part I saw but didn't immediately grok was the importance of the master thread having an ID that can be sent to the worker. The key that I'd missed was that the worker can send scripts to the master just as easily as the master can send scripts to the worker; the worker just usually doesn't know the master's Thread ID.

    Once the ID is passed to it, the producer thread can therefore use thread::send to call a proc in the master to handle the data, and it becomes an event in the master thread, just as I'd desired. It's not the way I've worked with threads in the past, but once understood it's powerful.

    0 讨论(0)
提交回复
热议问题