如果调用tkwait/vwait,Tcl_DoOneEvent将被阻止 有一个外部的C++函数,它从TCL/TK调用,并在相当长的时间内做一些事情。Tcl调用者必须获得该函数的结果,以便等待该函数完成。为了避免GUI的阻塞,C++函数在其正文中实现了某种事件循环: while (m_curSyncProc.isRunning()) { const clock_t tm = clock(); while (Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT) > 0) {} // <- stuck here in case of tkwait/vwait // Pause for 10 ms to avoid 100% CPU usage if (double(clock() - tm) / CLOCKS_PER_SEC < 0.005) { nanosleep(10000); } } while(m_curSyncProc.isRunning()){ 常数时钟=时钟(); while(Tcl_DoOneEvent(Tcl_ALL_EVENTS | Tcl_DONT_WAIT)>0){}/

如果调用tkwait/vwait,Tcl_DoOneEvent将被阻止 有一个外部的C++函数,它从TCL/TK调用,并在相当长的时间内做一些事情。Tcl调用者必须获得该函数的结果,以便等待该函数完成。为了避免GUI的阻塞,C++函数在其正文中实现了某种事件循环: while (m_curSyncProc.isRunning()) { const clock_t tm = clock(); while (Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT) > 0) {} // <- stuck here in case of tkwait/vwait // Pause for 10 ms to avoid 100% CPU usage if (double(clock() - tm) / CLOCKS_PER_SEC < 0.005) { nanosleep(10000); } } while(m_curSyncProc.isRunning()){ 常数时钟=时钟(); while(Tcl_DoOneEvent(Tcl_ALL_EVENTS | Tcl_DONT_WAIT)>0){}/,tcl,Tcl,小心!这是一个复杂的话题 Tcl_DoOneEvent()调用本质上就是vwait、tkwait和update的包装(传递不同的标志并设置不同的回调)。对其中任何一个的嵌套调用都会创建嵌套的事件循环;除非非常小心,否则您不会真正想要这些事件循环。事件循环只有在不处理任何活动事件回调时才会终止,如果这些事件回调创建了内部事件循环,则在内部事件循环完成之前,外部事件循环将无法执行任何操作。 当您控制外部事件循环(以一种非常低效的方式,但哦,好吧)时,您真的希望内部事件循环根本不运行。有三种可能的方法

小心!这是一个复杂的话题

Tcl_DoOneEvent()
调用本质上就是
vwait
tkwait
update
的包装(传递不同的标志并设置不同的回调)。对其中任何一个的嵌套调用都会创建嵌套的事件循环;除非非常小心,否则您不会真正想要这些事件循环。事件循环只有在不处理任何活动事件回调时才会终止,如果这些事件回调创建了内部事件循环,则在内部事件循环完成之前,外部事件循环将无法执行任何操作。

当您控制外部事件循环(以一种非常低效的方式,但哦,好吧)时,您真的希望内部事件循环根本不运行。有三种可能的方法来处理这个问题;我怀疑第三种方法(协程)最适合你,第一个是你真正想要避免的,但这绝对是你的决定

1.继续通过 您可以将内部代码重写为延续传递样式(一大堆过程,通过状态机/工作流一步一步地传递),这样它就不会真正调用
vwait
(和朋友)。家族中唯一一个趋向于模糊安全的是
updateidletasks
(这实际上只是
Tcl\u DoOneEvent(Tcl\u IDLE\u EVENTS | Tcl\u DONT\u WAIT)
)来处理Tk内部生成的更改

在Tcl 8.5之前,此选项是您的主要选择,而且需要做大量工作

2.螺纹 您可以移动到多线程应用程序。这可能很容易…也可能非常困难;细节取决于您在整个应用程序中所做的检查

如果走这条路,请记住Tcl解释器和Tcl值完全是线程绑定的;它们在内部使用特定于线程的数据,以便避免大型全局锁。这意味着Tcl中的线程设置成本相对较高,但实际上事后非常有效地使用多个CPU;线程池是一种非常常见的方法。

3.合作项目 从8.6开始,您可以将内部代码放在一个协同程序中。默认情况下,8.6中的几乎所有内容都是协同程序感知的(“非递归”在我们的内部行话中)(包括您通常不会想到的命令,例如
source
)完成后,您可以使用Tcllib中的等价项替换
vwait
调用,事情通常会“正常工作”。(例如,
vwait var
变成
coroutine::vwait var
,而
after 123
变成
coroutine::after 123

唯一没有直接替换的是
tkwait窗口
tkwait可见性
;您需要模拟等待
事件的情况(后者不常见,因为在某些平台上不支持),您可以通过
bind
对那些只设置变量的对象执行一个简单的回调,您可以
coroutine::vwait
打开变量(这基本上就是
tkwait
内部所做的一切)


在某些情况下,协程可能会变得混乱,例如当您有不支持协程的C代码时。在Tcl中,这些代码发挥作用的主要地方是
trace
回调、解释器间调用和通道的脚本实现;问题是这些代码背后的内部API相当复杂准备好了(特别是通道),没有人愿意涉入并启用非递归实现。

Coroutines(在Tcl中是深层次的Coroutines,与其他一些通用语言不同)允许您使用普通代码并将其内部转换为继续传递代码,而不需要显式地写出它;语言运行库有效地为您提供了重量级。结果非常类似于轻量级的协作线程。谢谢您的选项,因此,不可能在C++侧解决问题,对吗?y、 我的假设正确吗?如果我选择协同路由方式,将不可能从使用
coroutine::vwait
而不是常规
vwait
的函数中获得结果,因为它将在该行中返回其调用者。例如,
tk\u messageBox
的结果。使用线程进行操作将是解决问题的方法C++端的事情。你最终会在一个线程中有效地运行TCL/TK,而C++代码则在另一个线程中运行(除了一些胶水之外)。