【问题标题】:TCL Set vwait variable from TCL C Thread and TCL Script ThreadTCL 从 TCL C 线程和 TCL 脚本线程设置 vwait 变量
【发布时间】:2017-11-30 09:59:59
【问题描述】:

我的 C++ 应用程序嵌入了 TCL (8.6.7),需要同时通过 Windows 命名管道 (dll) 和 TCP 套接字 (TCL 脚本) 获取数据。我想使用TCL线程通过管道和套接字实现同时数据采集。通过 TCL C api 的一个线程 (threadPipe) 将管理从命名管道读取的数据,而通过 TCL 脚本 (threadTclSocket) 的另一个线程将管理套接字。主线程(C++ 应用程序)通过 TCL 脚本启动数据采集,并在其 TCL 脚本中使用 vwait forever 等待(非阻塞)工作线程。

我的问题:

  • 如何获取 TCL C 线程 (threadPipe) 和通过 TCL 脚本 (threadTclSocket) 的线程来设置变量:主应用程序的forever 当它们完成以便主线程可以退出其事件循环?
  • 我读过Tcl_ThreadQueueEventTcl_ThreadAlert,但我不明白它们如何用于在主线程中设置变量forever

任何建议将不胜感激。

【问题讨论】:

  • 您可以使用 twapi 扩展在 Tcl 中使用命名管道。
  • @JohannesKuhn 谢谢,命名管道已经实现为 dll。
  • 通常的习惯用法类似于while {$waitcount < 2} {vwait waitcount},并在其他线程完成时增加变量waitcount
  • 谢谢@JohannesKuhn。我目前的困难是如何让 TCL C 线程调用来设置变量waitcount 并将控制权返回给主 TCL 解释器。在设置 TCL_EventProc 中的变量后,它似乎挂起。
  • 每个线程完成后,应该使用Tcl_ThreadQueueEvent在使用Tcl_Evalincr ::waitcount的主线程中排队一个事件。

标签: tcl


【解决方案1】:

您可能应该使用 twapi 命名管道实现。我怀疑您的命名管道读取代码没有将此显示为支持 tcl fileevent 异步 API 的通道。如果你有这样的支持,就像在 twapi 版本中找到的那样,那么你根本不需要额外的线程。您只需使用fileevent 并在数据到达通道时调用一些 tcl 过程。

如果您坚持使用现有代码,那么您使用 Tcl_ThreadQueueEvent 将事件发布到 tcl 通知程序,以便解释器线程在处理该事件时调用一个函数。这将控制转移到解释器线程。这是我之前写的一个例子:

static void MailslotSignalledProc(void *clientData)
{
    MailslotData *slotPtr = (MailslotData *)clientData;
    MailslotEvent *evPtr = NULL;
    DWORD cbRead, cbSize = 0, cQueued = 0;
    BOOL br = FALSE;

    Tcl_GetLongFromObj(NULL, slotPtr->sizeObj, &cbSize);
    GetOverlappedResult(slotPtr->handle, &slotPtr->ov, &cbRead,  FALSE);
    do
    {
        evPtr = (MailslotEvent *)ckalloc(sizeof(MailslotEvent));
        evPtr->header.proc = EventProc;
        evPtr->header.nextPtr = NULL;
        evPtr->interp = slotPtr->pkgPtr->interp;
        Tcl_Preserve(evPtr->interp);
        evPtr->slotPtr = slotPtr;
        evPtr->messageObj = Tcl_NewByteArrayObj(slotPtr->message, cbRead);
        Tcl_IncrRefCount(evPtr->messageObj);
        Tcl_ThreadQueueEvent(slotPtr->wait.tid, (Tcl_Event *)evPtr, TCL_QUEUE_TAIL);

        /*
         * Schedule another read on the mailslot. This may return
         * immediately if data is already available. We also rate-limit
         * by enforcing a wait after 64 immediate messages.
         */
        br = ReadFile(slotPtr->handle, slotPtr->message, cbSize, &cbRead, &slotPtr->ov);
        ++cQueued;
    } while (br && cQueued < 64);

    Tcl_ThreadAlert(slotPtr->wait.tid);
    return;
}

值得注意的是,我们有一个“继承”自 Tcl_Event 的结构,并允许我们将自己的数据放入事件中。我们可以发布一些事件并在完成后使用Tcl_ThreadAlert 唤醒目标。目标函数(在本例中为 EventProc)将传递此结构,但将在解释器线程上运行,因此我们必须注意 Tcl_Obj 引用计数。

在您的情况下,您可能只想在事件过程中设置永久变量。

如果有用,您可以查看whole file

但是,您确定不应该只在正确编写的命名管道通道上使用 fileevent 吗?

【讨论】:

  • 感谢您的回答。您怀疑命名管道不提供任何事件是正确的,但在这种情况下,我几乎没有选择使用它。我已经实现了您对Tcl_EventTcl_EventProc 的建议,这似乎可行,因为调用了Tcl_EventProc。但似乎程序卡在Tcl_EventProc 并且控制权没有返回到主 TCL 解释器。
  • 您通常根本无法在线程之间发送 Tcl_Obj;它们的默认分配器是强线程耦合的。
【解决方案2】:

感谢您的所有建议,我已经解决了我的问题。

用TCL脚本设置vwait foreverforever变量很简单,只需要使用forever作为thread::send的结果,让TCL主线程等待它。对于TCL C 线程设置forever 立即在TCL_EventProc 挂起主TCL 解释器。为了解决这个问题,我延迟了forever 的设置,直到事件循环使用after 空闲,如下所示:

after idle {set forever thread}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多