TCL-C API:Tcl_LinkVar函数使用

我试图将一个Tcl变量链接到一个C变量,以便在C线程创建过程中将指针传递给最新,并有一个TCL-C线程共享变量(我不认为我可以使用本机TCL线程共享变量函数) 。 链接这两个变量我有些困难。 我是这样做的:

#Tcl code, calling the C function: set linkedVar 98 puts "linkedVar: $linkedVar" load [file join [pwd] libCextension[info sharedlibextension]] set threadId [createThreadC] puts "Created thread n° $threadId" puts "linkedVar: $linkedVar" 

createThreadC函数创建一个C线程,返回其ID并尝试使用linkedVar创建一个链接。

 // C function called by Tcl static int createThreadC_Cmd( ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { int linkedVar=2; Tcl_LinkVar(interp, "linkedVar", (char *) &linkedVar, TCL_LINK_INT); linkedVar=1; ... # Thread creation, return Tcl object with thread ID ... return TCL_OK; } 

这是输出:

 linkedVar: 98 Created thread n° -1227199680 linkedVar: 35 

linkedVar值改变了,因为C程序必须,但是它存储了错误的变量,应该是1而不是35.是(char *) &linkedVar是错误的吗?

我打算和Donal说同样的东西,但也写了一个演示。 所以这就是 – 基本上你的链接变量生命周期应该与解释器生命周期匹配。

 #include  typedef struct Shared { Tcl_Interp *interp; int id; } Shared; static int UpdateCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Shared *sharedPtr = (Shared *)clientData; if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } ++sharedPtr->id; return TCL_OK; } static void DeleteProc(ClientData clientData) { Shared *sharedPtr = (Shared *)clientData; Tcl_UnlinkVar(sharedPtr->interp, "shared_id"); Tcl_Release(sharedPtr->interp); Tcl_Free(clientData); } int DLLEXPORT Testlink_Init(Tcl_Interp *interp) { Shared *sharedPtr; if (Tcl_InitStubs(interp, "8.4", 0) == NULL) { return TCL_ERROR; } sharedPtr = (Shared *)Tcl_Alloc(sizeof(Shared)); sharedPtr->interp = interp; sharedPtr->id = 0; Tcl_Preserve(sharedPtr->interp); Tcl_LinkVar(interp, "shared_id", (char *)&sharedPtr->id, TCL_LINK_INT); Tcl_CreateObjCommand(interp, "update_shared", UpdateCmd, sharedPtr, DeleteProc); Tcl_PkgProvide(interp, "testlink", "1.0"); return TCL_OK; } 

用法(使用msvc 6构建):

 C:\src>cl -nologo -Od -MD -I\opt\tcl\include -DUSE_TCL_STUBS -c tcl_link.c tcl_link.c C:\src>link -dll -debug -out:tcl_link.dll tcl_link.obj \opt\tcl\lib\tclstub85.lib Microsoft (R) Incremental Linker Version 6.00.8447 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. C:\src>tclsh % load tcl_link.dll testlink % set shared_id 0 % update_shared % set shared_id 1 

这显示了一种通过利用命令清理function来清理事物的方法。

如果你使用多个线程并且还有Tcl解释器,你必须非常小心。 Tcl interp与创建它的线程相关联。 因此,如果您希望在C线程之间传递Shared结构,则应该松开interp成员。 在那种情况下,我的应用程序将处理此结构生命周期。 如果您的Shared结构的生命周期比任何具有clientData的命令的解释器长,那么一切都会正常,您不需要在解释器中进行清理。

几乎正确使用Tcl_LinkVar ; 您的原始代码是类型正确的。 但那不是什么错!

问题是你在一个Tcl解释器(具有相当长的生命周期)和C堆栈上的变量之间进行链接,并且生命周期很短。 在createThreadC_Cmd之后,链接指向未使用的堆栈,并且通常会在之后立即用于其他内容。 这是正式未定义的行为,而且非常糟糕 。 您需要做的是确保C变量的生命周期至少与解释器的生命周期一样长。

最简单的解决方法是使用全局(或static本地)变量。 唯一createThreadC_Cmd是,在createThreadC_Cmd所有调用之间共享同一个变量; 有时这根本不是问题,但我怀疑你的情况并非如此。 所以你需要在其他地方分配一些空间。 如果您不希望自己创建的解释器消失,最简单的方法就是使用malloc来获得一个小空间,然后将链接指向它; 然后你可以泄漏记忆并不再担心(这是不洁净的,但很容易做到)。 如果你想要清理,你几乎做同样的事情,但注册一个适当的关闭钩子, free内存; 根据实际情况,Tcl有三种关闭钩子:

  1. 使用Tcl_CallWhenDeleted创建解释器关闭挂钩
  2. 使用Tcl_CreateThreadExitHandler创建线程关闭挂钩
  3. 使用Tcl_CreateExitHandler创建进程/库关闭挂钩(除非你需要进行超级清理,否则不需要它来删除内存;警告,在调用这些内存时很难获得内存删除) 。

我不太确定哪个适合你; 这取决于你分享变量的广泛程度。 (我希望你不打算在各个线程之间分享它;这在设计上不会很好。)