Как один поток Tcl может вызвать событие в другом? - PullRequest
2 голосов
/ 14 декабря 2010

У меня есть система с несколькими интерфейсами ввода / вывода, и я собираю вывод всех из них в общий журнал. Два интерфейса работают через каналы с хорошим поведением, которые открываются как файловые объекты, и ими можно управлять на основе событий с помощью «fileevent readable». Две другие проблемы.

Это относится к поставляемой поставщиком библиотеке, которую кто-то еще любезно обернул в пакет Tcl (snoopy, FWIW). Однако единственный доступ для чтения - это блокирующий вызов, и в пакете нет ничего, что могло бы вызвать событие, эквивалентное fileevent.

Я выяснил, как создать отдельный поток для блокировки чтения, получить результат и поместить его в очередь сообщений для основного потока. Но наличие блока основного потока при чтении очереди, казалось бы, отрицательно сказывается на цели, тем более что существует две очереди, на которые он должен был бы блокироваться. И я не смог заставить читателя сгенерировать событие, которое может вызвать основной поток для чтения очереди.

Я просмотрел вики tcl.tk безрезультатно. Я пытался использовать библиотеку uevent для генерации события при отправке сообщения, но событие переходит к потоку записи вместо потока чтения, что действительно не помогает. Кажется, что должно быть какое-то решение, связанное с переменной условия Thread, но до сих пор я не смог найти подходящий шаблон проектирования для этого использования библиотеки.

Если ничего не получится, я вернусь к событию Tk, но я пытаюсь не допустить этого к Tk, поскольку это автоматизированная система без GUI, и любое упоминание Tk толкает tclsh к желанию и всплывает окно с графическим интерфейсом.

Я чувствую, что я рядом, но что-то упускаю.

Ответы [ 2 ]

5 голосов
/ 14 декабря 2010

Во-первых, основной поток должен запустить цикл обработки событий для получения событий. Идиоматический способ сделать это - использовать vwait forever после завершения настройки программы (я полагаю, вы не собираетесь записывать в эту переменную), но если вы запускаете Tk, у вас уже есть цикл обработки событий (GUI *) 1002 * нужно циклы событий).

Существует два способа обмена сообщениями между потоками.

Поток событий

Команда thread::send использует события для отправки кода для выполнения (сообщения) между потоками. Все, что вам нужно сделать, это сообщить рабочему потоку, какой идентификатор основного потока, чтобы он знал, куда отправлять. Обратите внимание, что вы можете отправлять событие асинхронно, например:

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

Трубопроводы

Если вы используете Tcl 8.6, вы можете использовать chan pipe для создания безымянного конвейера ОС. Затем вы можете использовать обычные файловые события и т. Д., Чтобы таким образом доставлять информацию из одного потока в другой.

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

Вероятно, проще использовать события потоков в ретроспективе! (Основным преимуществом конвейера является то, что вы также можете при необходимости поместить работника в другой процесс.)

2 голосов
/ 15 декабря 2010

Я наконец ухмыльнулся тому, что Донал говорил о событиях Нити.Я обвиняю неадекватный утренний кофеин в том, что он не получил его в первый раз.

Все предыдущие примеры thread :: send, которые я видел, касались мастера, отправляющего сценарии в рабочий поток.В этом случае рабочий поток должен отправить сценарии обратно на мастер, где этот сценарий будет вызываться в [fileevent readable], если это был канал.

Вот мой тестовый код для взаимодействия:

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

Часть, которую я увидел, но не сразу запустил, была важностью того, что главный поток имеет идентификатор, который можно отправить работнику,Ключ, который я пропустил, состоял в том, что работник может отправлять сценарии мастеру так же легко, как мастер может отправлять сценарии работнику;рабочий обычно просто не знает идентификатор потока мастера.

Как только ID передан ему, поток-производитель может поэтому использовать thread :: send для вызова proc в master-файле для обработки данных, и он становится событием в master-потоке, как ябуду желанным.Я не работал с потоками в прошлом, но однажды понял, что это мощно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...